2017-07-23 15:14:38 +00:00
|
|
|
use back::bytecode::{DecodedBytecode, RLIB_BYTECODE_EXTENSION};
|
2018-10-23 15:01:35 +00:00
|
|
|
use rustc_codegen_ssa::back::symbol_export;
|
|
|
|
use rustc_codegen_ssa::back::write::{ModuleConfig, CodegenContext, pre_lto_bitcode_filename};
|
|
|
|
use rustc_codegen_ssa::back::lto::{SerializedModule, LtoModuleCodegen, ThinShared, ThinModule};
|
2018-11-16 11:45:28 +00:00
|
|
|
use rustc_codegen_ssa::traits::*;
|
2018-10-23 15:01:35 +00:00
|
|
|
use back::write::{self, DiagnosticHandlers, with_llvm_pmb, save_temp_bitcode, get_llvm_opt_level};
|
2017-07-24 11:54:18 +00:00
|
|
|
use errors::{FatalError, Handler};
|
2014-07-07 04:43:22 +00:00
|
|
|
use llvm::archive_ro::ArchiveRO;
|
2018-09-25 15:52:03 +00:00
|
|
|
use llvm::{self, True, False};
|
2018-08-31 13:18:08 +00:00
|
|
|
use rustc::dep_graph::WorkProduct;
|
2018-09-18 14:33:24 +00:00
|
|
|
use rustc::dep_graph::cgu_reuse_tracker::CguReuse;
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
use rustc::hir::def_id::LOCAL_CRATE;
|
2017-09-13 20:22:20 +00:00
|
|
|
use rustc::middle::exported_symbols::SymbolExportLevel;
|
2018-01-16 23:02:31 +00:00
|
|
|
use rustc::session::config::{self, Lto};
|
2017-12-03 13:21:23 +00:00
|
|
|
use rustc::util::common::time_ext;
|
2018-08-31 13:18:08 +00:00
|
|
|
use rustc_data_structures::fx::FxHashMap;
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
use time_graph::Timeline;
|
2018-10-23 15:01:35 +00:00
|
|
|
use {ModuleLlvm, LlvmCodegenBackend};
|
2018-10-01 16:07:04 +00:00
|
|
|
use rustc_codegen_ssa::{ModuleCodegen, ModuleKind};
|
Implement LTO
This commit implements LTO for rust leveraging LLVM's passes. What this means
is:
* When compiling an rlib, in addition to insdering foo.o into the archive, also
insert foo.bc (the LLVM bytecode) of the optimized module.
* When the compiler detects the -Z lto option, it will attempt to perform LTO on
a staticlib or binary output. The compiler will emit an error if a dylib or
rlib output is being generated.
* The actual act of performing LTO is as follows:
1. Force all upstream libraries to have an rlib version available.
2. Load the bytecode of each upstream library from the rlib.
3. Link all this bytecode into the current LLVM module (just using llvm
apis)
4. Run an internalization pass which internalizes all symbols except those
found reachable for the local crate of compilation.
5. Run the LLVM LTO pass manager over this entire module
6a. If assembling an archive, then add all upstream rlibs into the output
archive. This ignores all of the object/bitcode/metadata files rust
generated and placed inside the rlibs.
6b. If linking a binary, create copies of all upstream rlibs, remove the
rust-generated object-file, and then link everything as usual.
As I have explained in #10741, this process is excruciatingly slow, so this is
*not* turned on by default, and it is also why I have decided to hide it behind
a -Z flag for now. The good news is that the binary sizes are about as small as
they can be as a result of LTO, so it's definitely working.
Closes #10741
Closes #10740
2013-12-03 07:19:29 +00:00
|
|
|
|
2014-02-26 17:58:41 +00:00
|
|
|
use libc;
|
Implement LTO
This commit implements LTO for rust leveraging LLVM's passes. What this means
is:
* When compiling an rlib, in addition to insdering foo.o into the archive, also
insert foo.bc (the LLVM bytecode) of the optimized module.
* When the compiler detects the -Z lto option, it will attempt to perform LTO on
a staticlib or binary output. The compiler will emit an error if a dylib or
rlib output is being generated.
* The actual act of performing LTO is as follows:
1. Force all upstream libraries to have an rlib version available.
2. Load the bytecode of each upstream library from the rlib.
3. Link all this bytecode into the current LLVM module (just using llvm
apis)
4. Run an internalization pass which internalizes all symbols except those
found reachable for the local crate of compilation.
5. Run the LLVM LTO pass manager over this entire module
6a. If assembling an archive, then add all upstream rlibs into the output
archive. This ignores all of the object/bitcode/metadata files rust
generated and placed inside the rlibs.
6b. If linking a binary, create copies of all upstream rlibs, remove the
rust-generated object-file, and then link everything as usual.
As I have explained in #10741, this process is excruciatingly slow, so this is
*not* turned on by default, and it is also why I have decided to hide it behind
a -Z flag for now. The good news is that the binary sizes are about as small as
they can be as a result of LTO, so it's definitely working.
Closes #10741
Closes #10740
2013-12-03 07:19:29 +00:00
|
|
|
|
2018-08-17 14:07:23 +00:00
|
|
|
use std::ffi::{CStr, CString};
|
2018-09-05 11:52:58 +00:00
|
|
|
use std::fs;
|
2017-12-16 16:20:54 +00:00
|
|
|
use std::ptr;
|
2017-07-23 15:14:38 +00:00
|
|
|
use std::slice;
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
use std::sync::Arc;
|
2014-07-31 13:05:08 +00:00
|
|
|
|
2016-11-30 15:03:42 +00:00
|
|
|
pub fn crate_type_allows_lto(crate_type: config::CrateType) -> bool {
|
|
|
|
match crate_type {
|
2018-07-26 17:13:11 +00:00
|
|
|
config::CrateType::Executable |
|
|
|
|
config::CrateType::Staticlib |
|
|
|
|
config::CrateType::Cdylib => true,
|
2016-11-30 15:03:42 +00:00
|
|
|
|
2018-07-26 17:13:11 +00:00
|
|
|
config::CrateType::Dylib |
|
|
|
|
config::CrateType::Rlib |
|
|
|
|
config::CrateType::ProcMacro => false,
|
2016-11-30 15:03:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-03 19:45:03 +00:00
|
|
|
fn prepare_lto(cgcx: &CodegenContext<LlvmCodegenBackend>,
|
|
|
|
timeline: &mut Timeline,
|
|
|
|
diag_handler: &Handler)
|
|
|
|
-> Result<(Vec<CString>, Vec<(SerializedModule<ModuleBuffer>, CString)>), FatalError>
|
2017-07-23 15:14:38 +00:00
|
|
|
{
|
2018-01-16 23:02:31 +00:00
|
|
|
let export_threshold = match cgcx.lto {
|
|
|
|
// We're just doing LTO for our one crate
|
|
|
|
Lto::ThinLocal => SymbolExportLevel::Rust,
|
|
|
|
|
|
|
|
// We're doing LTO for the entire crate graph
|
2018-09-04 15:57:17 +00:00
|
|
|
Lto::Fat | Lto::Thin => {
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
symbol_export::crates_export_threshold(&cgcx.crate_types)
|
Implement LTO
This commit implements LTO for rust leveraging LLVM's passes. What this means
is:
* When compiling an rlib, in addition to insdering foo.o into the archive, also
insert foo.bc (the LLVM bytecode) of the optimized module.
* When the compiler detects the -Z lto option, it will attempt to perform LTO on
a staticlib or binary output. The compiler will emit an error if a dylib or
rlib output is being generated.
* The actual act of performing LTO is as follows:
1. Force all upstream libraries to have an rlib version available.
2. Load the bytecode of each upstream library from the rlib.
3. Link all this bytecode into the current LLVM module (just using llvm
apis)
4. Run an internalization pass which internalizes all symbols except those
found reachable for the local crate of compilation.
5. Run the LLVM LTO pass manager over this entire module
6a. If assembling an archive, then add all upstream rlibs into the output
archive. This ignores all of the object/bitcode/metadata files rust
generated and placed inside the rlibs.
6b. If linking a binary, create copies of all upstream rlibs, remove the
rust-generated object-file, and then link everything as usual.
As I have explained in #10741, this process is excruciatingly slow, so this is
*not* turned on by default, and it is also why I have decided to hide it behind
a -Z flag for now. The good news is that the binary sizes are about as small as
they can be as a result of LTO, so it's definitely working.
Closes #10741
Closes #10740
2013-12-03 07:19:29 +00:00
|
|
|
}
|
2018-01-16 23:02:31 +00:00
|
|
|
|
|
|
|
Lto::No => panic!("didn't request LTO but we're doing LTO"),
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
};
|
2016-11-30 15:03:42 +00:00
|
|
|
|
2018-02-27 16:52:07 +00:00
|
|
|
let symbol_filter = &|&(ref name, level): &(String, SymbolExportLevel)| {
|
2017-09-13 20:22:20 +00:00
|
|
|
if level.is_below_threshold(export_threshold) {
|
2016-11-30 15:03:42 +00:00
|
|
|
let mut bytes = Vec::with_capacity(name.len() + 1);
|
|
|
|
bytes.extend(name.bytes());
|
|
|
|
Some(CString::new(bytes).unwrap())
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
};
|
2018-02-14 20:08:21 +00:00
|
|
|
let exported_symbols = cgcx.exported_symbols
|
|
|
|
.as_ref().expect("needs exported symbols for LTO");
|
|
|
|
let mut symbol_white_list = exported_symbols[&LOCAL_CRATE]
|
2016-11-30 15:03:42 +00:00
|
|
|
.iter()
|
|
|
|
.filter_map(symbol_filter)
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
.collect::<Vec<CString>>();
|
|
|
|
timeline.record("whitelist");
|
2017-10-11 18:19:59 +00:00
|
|
|
info!("{} symbols to preserve in this crate", symbol_white_list.len());
|
2016-11-30 15:03:42 +00:00
|
|
|
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
// If we're performing LTO for the entire crate graph, then for each of our
|
|
|
|
// upstream dependencies, find the corresponding rlib and load the bitcode
|
|
|
|
// from the archive.
|
|
|
|
//
|
|
|
|
// We save off all the bytecode and LLVM module ids for later processing
|
|
|
|
// with either fat or thin LTO
|
2017-07-23 15:14:38 +00:00
|
|
|
let mut upstream_modules = Vec::new();
|
2018-01-16 23:02:31 +00:00
|
|
|
if cgcx.lto != Lto::ThinLocal {
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
if cgcx.opts.cg.prefer_dynamic {
|
|
|
|
diag_handler.struct_err("cannot prefer dynamic linking when performing LTO")
|
|
|
|
.note("only 'staticlib', 'bin', and 'cdylib' outputs are \
|
|
|
|
supported with LTO")
|
|
|
|
.emit();
|
|
|
|
return Err(FatalError)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure we actually can run LTO
|
|
|
|
for crate_type in cgcx.crate_types.iter() {
|
|
|
|
if !crate_type_allows_lto(*crate_type) {
|
|
|
|
let e = diag_handler.fatal("lto can only be run for executables, cdylibs and \
|
|
|
|
static library outputs");
|
|
|
|
return Err(e)
|
|
|
|
}
|
2014-09-17 23:18:12 +00:00
|
|
|
}
|
Implement LTO
This commit implements LTO for rust leveraging LLVM's passes. What this means
is:
* When compiling an rlib, in addition to insdering foo.o into the archive, also
insert foo.bc (the LLVM bytecode) of the optimized module.
* When the compiler detects the -Z lto option, it will attempt to perform LTO on
a staticlib or binary output. The compiler will emit an error if a dylib or
rlib output is being generated.
* The actual act of performing LTO is as follows:
1. Force all upstream libraries to have an rlib version available.
2. Load the bytecode of each upstream library from the rlib.
3. Link all this bytecode into the current LLVM module (just using llvm
apis)
4. Run an internalization pass which internalizes all symbols except those
found reachable for the local crate of compilation.
5. Run the LLVM LTO pass manager over this entire module
6a. If assembling an archive, then add all upstream rlibs into the output
archive. This ignores all of the object/bitcode/metadata files rust
generated and placed inside the rlibs.
6b. If linking a binary, create copies of all upstream rlibs, remove the
rust-generated object-file, and then link everything as usual.
As I have explained in #10741, this process is excruciatingly slow, so this is
*not* turned on by default, and it is also why I have decided to hide it behind
a -Z flag for now. The good news is that the binary sizes are about as small as
they can be as a result of LTO, so it's definitely working.
Closes #10741
Closes #10740
2013-12-03 07:19:29 +00:00
|
|
|
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() {
|
2018-02-14 20:08:21 +00:00
|
|
|
let exported_symbols = cgcx.exported_symbols
|
|
|
|
.as_ref().expect("needs exported symbols for LTO");
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
symbol_white_list.extend(
|
2018-02-14 20:08:21 +00:00
|
|
|
exported_symbols[&cnum]
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
.iter()
|
|
|
|
.filter_map(symbol_filter));
|
|
|
|
|
|
|
|
let archive = ArchiveRO::open(&path).expect("wanted an rlib");
|
|
|
|
let bytecodes = archive.iter().filter_map(|child| {
|
|
|
|
child.ok().and_then(|c| c.name().map(|name| (name, c)))
|
|
|
|
}).filter(|&(name, _)| name.ends_with(RLIB_BYTECODE_EXTENSION));
|
|
|
|
for (name, data) in bytecodes {
|
|
|
|
info!("adding bytecode {}", name);
|
|
|
|
let bc_encoded = data.data();
|
|
|
|
|
2017-12-03 13:21:23 +00:00
|
|
|
let (bc, id) = time_ext(cgcx.time_passes, None, &format!("decode {}", name), || {
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
match DecodedBytecode::new(bc_encoded) {
|
|
|
|
Ok(b) => Ok((b.bytecode(), b.identifier().to_string())),
|
|
|
|
Err(e) => Err(diag_handler.fatal(&e)),
|
|
|
|
}
|
|
|
|
})?;
|
|
|
|
let bc = SerializedModule::FromRlib(bc);
|
|
|
|
upstream_modules.push((bc, CString::new(id).unwrap()));
|
|
|
|
}
|
|
|
|
timeline.record(&format!("load: {}", path.display()));
|
|
|
|
}
|
|
|
|
}
|
2017-07-23 15:14:38 +00:00
|
|
|
|
2018-12-03 19:45:03 +00:00
|
|
|
Ok((symbol_white_list, upstream_modules))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Performs fat LTO by merging all modules into a single one and returning it
|
|
|
|
/// for further optimization.
|
|
|
|
pub(crate) fn run_fat(cgcx: &CodegenContext<LlvmCodegenBackend>,
|
|
|
|
modules: Vec<ModuleCodegen<ModuleLlvm>>,
|
|
|
|
timeline: &mut Timeline)
|
2018-12-03 19:50:39 +00:00
|
|
|
-> Result<LtoModuleCodegen<LlvmCodegenBackend>, FatalError>
|
2018-12-03 19:45:03 +00:00
|
|
|
{
|
|
|
|
let diag_handler = cgcx.create_diag_handler();
|
|
|
|
let (symbol_white_list, upstream_modules) = prepare_lto(cgcx, timeline, &diag_handler)?;
|
2018-08-20 15:13:01 +00:00
|
|
|
let symbol_white_list = symbol_white_list.iter()
|
|
|
|
.map(|c| c.as_ptr())
|
|
|
|
.collect::<Vec<_>>();
|
2018-12-03 19:50:39 +00:00
|
|
|
fat_lto(cgcx, &diag_handler, modules, upstream_modules, &symbol_white_list, timeline)
|
2018-12-03 19:45:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Performs thin LTO by performing necessary global analysis and returning two
|
|
|
|
/// lists, one of the modules that need optimization and another for modules that
|
|
|
|
/// can simply be copied over from the incr. comp. cache.
|
|
|
|
pub(crate) fn run_thin(cgcx: &CodegenContext<LlvmCodegenBackend>,
|
2018-12-04 15:24:20 +00:00
|
|
|
modules: Vec<(String, ThinBuffer)>,
|
2018-12-03 19:45:03 +00:00
|
|
|
cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
|
|
|
|
timeline: &mut Timeline)
|
|
|
|
-> Result<(Vec<LtoModuleCodegen<LlvmCodegenBackend>>, Vec<WorkProduct>), FatalError>
|
|
|
|
{
|
|
|
|
let diag_handler = cgcx.create_diag_handler();
|
|
|
|
let (symbol_white_list, upstream_modules) = prepare_lto(cgcx, timeline, &diag_handler)?;
|
|
|
|
let symbol_white_list = symbol_white_list.iter()
|
|
|
|
.map(|c| c.as_ptr())
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
if cgcx.opts.debugging_opts.cross_lang_lto.enabled() {
|
|
|
|
unreachable!("We should never reach this case if the LTO step \
|
|
|
|
is deferred to the linker");
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
}
|
2018-12-03 19:45:03 +00:00
|
|
|
thin_lto(cgcx,
|
|
|
|
&diag_handler,
|
|
|
|
modules,
|
|
|
|
upstream_modules,
|
|
|
|
cached_modules,
|
|
|
|
&symbol_white_list,
|
|
|
|
timeline)
|
2017-07-23 15:14:38 +00:00
|
|
|
}
|
|
|
|
|
2018-12-04 15:24:20 +00:00
|
|
|
pub(crate) fn prepare_thin(
|
|
|
|
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
|
|
|
module: ModuleCodegen<ModuleLlvm>
|
|
|
|
) -> (String, ThinBuffer) {
|
|
|
|
let name = module.name.clone();
|
|
|
|
let buffer = ThinBuffer::new(module.module_llvm.llmod());
|
|
|
|
|
|
|
|
// We emit the module after having serialized it into a ThinBuffer
|
|
|
|
// because only then it will contain the ThinLTO module summary.
|
|
|
|
if let Some(ref incr_comp_session_dir) = cgcx.incr_comp_session_dir {
|
|
|
|
if cgcx.config(module.kind).emit_pre_thin_lto_bc {
|
|
|
|
let path = incr_comp_session_dir
|
|
|
|
.join(pre_lto_bitcode_filename(&name));
|
|
|
|
|
|
|
|
fs::write(&path, buffer.data()).unwrap_or_else(|e| {
|
|
|
|
panic!("Error writing pre-lto-bitcode file `{}`: {}",
|
|
|
|
path.display(),
|
|
|
|
e);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
(name, buffer)
|
|
|
|
}
|
|
|
|
|
2018-10-23 15:01:35 +00:00
|
|
|
fn fat_lto(cgcx: &CodegenContext<LlvmCodegenBackend>,
|
2017-07-23 15:14:38 +00:00
|
|
|
diag_handler: &Handler,
|
2018-09-25 15:52:03 +00:00
|
|
|
mut modules: Vec<ModuleCodegen<ModuleLlvm>>,
|
2018-10-23 15:01:35 +00:00
|
|
|
mut serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>,
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
symbol_white_list: &[*const libc::c_char],
|
|
|
|
timeline: &mut Timeline)
|
2018-12-03 19:50:39 +00:00
|
|
|
-> Result<LtoModuleCodegen<LlvmCodegenBackend>, FatalError>
|
2017-07-23 15:14:38 +00:00
|
|
|
{
|
|
|
|
info!("going for a fat lto");
|
|
|
|
|
|
|
|
// Find the "costliest" module and merge everything into that codegen unit.
|
|
|
|
// All the other modules will be serialized and reparsed into the new
|
|
|
|
// context, so this hopefully avoids serializing and parsing the largest
|
|
|
|
// codegen unit.
|
|
|
|
//
|
|
|
|
// Additionally use a regular module as the base here to ensure that various
|
|
|
|
// file copy operations in the backend work correctly. The only other kind
|
|
|
|
// of module here should be an allocator one, and if your crate is smaller
|
|
|
|
// than the allocator module then the size doesn't really matter anyway.
|
|
|
|
let (_, costliest_module) = modules.iter()
|
|
|
|
.enumerate()
|
|
|
|
.filter(|&(_, module)| module.kind == ModuleKind::Regular)
|
|
|
|
.map(|(i, module)| {
|
|
|
|
let cost = unsafe {
|
2018-08-20 15:13:01 +00:00
|
|
|
llvm::LLVMRustModuleCost(module.module_llvm.llmod())
|
2017-07-23 15:14:38 +00:00
|
|
|
};
|
|
|
|
(cost, i)
|
|
|
|
})
|
|
|
|
.max()
|
2018-05-08 13:10:16 +00:00
|
|
|
.expect("must be codegen'ing at least one module");
|
2017-07-23 15:14:38 +00:00
|
|
|
let module = modules.remove(costliest_module);
|
|
|
|
let mut serialized_bitcode = Vec::new();
|
2018-06-27 14:57:25 +00:00
|
|
|
{
|
2018-07-17 23:20:51 +00:00
|
|
|
let (llcx, llmod) = {
|
2018-08-20 15:13:01 +00:00
|
|
|
let llvm = &module.module_llvm;
|
2018-07-17 23:20:51 +00:00
|
|
|
(&llvm.llcx, llvm.llmod())
|
|
|
|
};
|
2018-08-14 15:55:22 +00:00
|
|
|
info!("using {:?} as a base module", module.name);
|
2018-06-27 14:57:25 +00:00
|
|
|
|
2018-07-17 23:20:51 +00:00
|
|
|
// The linking steps below may produce errors and diagnostics within LLVM
|
|
|
|
// which we'd like to handle and print, so set up our diagnostic handlers
|
|
|
|
// (which get unregistered when they go out of scope below).
|
|
|
|
let _handler = DiagnosticHandlers::new(cgcx, diag_handler, llcx);
|
|
|
|
|
2018-06-27 14:57:25 +00:00
|
|
|
// For all other modules we codegened we'll need to link them into our own
|
|
|
|
// bitcode. All modules were codegened in their own LLVM context, however,
|
|
|
|
// and we want to move everything to the same LLVM context. Currently the
|
|
|
|
// way we know of to do that is to serialize them to a string and them parse
|
|
|
|
// them later. Not great but hey, that's why it's "fat" LTO, right?
|
2018-10-06 09:45:11 +00:00
|
|
|
serialized_modules.extend(modules.into_iter().map(|module| {
|
2018-08-20 15:13:01 +00:00
|
|
|
let buffer = ModuleBuffer::new(module.module_llvm.llmod());
|
2018-08-14 15:55:22 +00:00
|
|
|
let llmod_id = CString::new(&module.name[..]).unwrap();
|
2018-10-06 09:45:11 +00:00
|
|
|
|
|
|
|
(SerializedModule::Local(buffer), llmod_id)
|
|
|
|
}));
|
2017-07-23 15:14:38 +00:00
|
|
|
|
2018-06-27 14:57:25 +00:00
|
|
|
// For all serialized bitcode files we parse them and link them in as we did
|
|
|
|
// above, this is all mostly handled in C++. Like above, though, we don't
|
|
|
|
// know much about the memory management here so we err on the side of being
|
|
|
|
// save and persist everything with the original module.
|
|
|
|
let mut linker = Linker::new(llmod);
|
|
|
|
for (bc_decoded, name) in serialized_modules {
|
|
|
|
info!("linking {:?}", name);
|
|
|
|
time_ext(cgcx.time_passes, None, &format!("ll link {:?}", name), || {
|
|
|
|
let data = bc_decoded.data();
|
|
|
|
linker.add(&data).map_err(|()| {
|
|
|
|
let msg = format!("failed to load bc of {:?}", name);
|
2018-10-06 09:42:14 +00:00
|
|
|
write::llvm_err(&diag_handler, &msg)
|
2018-06-27 14:57:25 +00:00
|
|
|
})
|
|
|
|
})?;
|
|
|
|
timeline.record(&format!("link {:?}", name));
|
|
|
|
serialized_bitcode.push(bc_decoded);
|
|
|
|
}
|
|
|
|
drop(linker);
|
2018-10-23 15:01:35 +00:00
|
|
|
save_temp_bitcode(&cgcx, &module, "lto.input");
|
Implement LTO
This commit implements LTO for rust leveraging LLVM's passes. What this means
is:
* When compiling an rlib, in addition to insdering foo.o into the archive, also
insert foo.bc (the LLVM bytecode) of the optimized module.
* When the compiler detects the -Z lto option, it will attempt to perform LTO on
a staticlib or binary output. The compiler will emit an error if a dylib or
rlib output is being generated.
* The actual act of performing LTO is as follows:
1. Force all upstream libraries to have an rlib version available.
2. Load the bytecode of each upstream library from the rlib.
3. Link all this bytecode into the current LLVM module (just using llvm
apis)
4. Run an internalization pass which internalizes all symbols except those
found reachable for the local crate of compilation.
5. Run the LLVM LTO pass manager over this entire module
6a. If assembling an archive, then add all upstream rlibs into the output
archive. This ignores all of the object/bitcode/metadata files rust
generated and placed inside the rlibs.
6b. If linking a binary, create copies of all upstream rlibs, remove the
rust-generated object-file, and then link everything as usual.
As I have explained in #10741, this process is excruciatingly slow, so this is
*not* turned on by default, and it is also why I have decided to hide it behind
a -Z flag for now. The good news is that the binary sizes are about as small as
they can be as a result of LTO, so it's definitely working.
Closes #10741
Closes #10740
2013-12-03 07:19:29 +00:00
|
|
|
|
2018-06-27 14:57:25 +00:00
|
|
|
// Internalize everything that *isn't* in our whitelist to help strip out
|
|
|
|
// more modules and such
|
2013-12-11 07:27:15 +00:00
|
|
|
unsafe {
|
2018-06-27 14:57:25 +00:00
|
|
|
let ptr = symbol_white_list.as_ptr();
|
|
|
|
llvm::LLVMRustRunRestrictionPass(llmod,
|
2018-10-06 09:37:28 +00:00
|
|
|
ptr as *const *const libc::c_char,
|
|
|
|
symbol_white_list.len() as libc::size_t);
|
2018-10-23 15:01:35 +00:00
|
|
|
save_temp_bitcode(&cgcx, &module, "lto.after-restriction");
|
2013-12-11 07:27:15 +00:00
|
|
|
}
|
2018-06-27 14:57:25 +00:00
|
|
|
|
|
|
|
if cgcx.no_landing_pads {
|
|
|
|
unsafe {
|
|
|
|
llvm::LLVMRustMarkAllFunctionsNounwind(llmod);
|
|
|
|
}
|
2018-10-23 15:01:35 +00:00
|
|
|
save_temp_bitcode(&cgcx, &module, "lto.after-nounwind");
|
2018-06-27 14:57:25 +00:00
|
|
|
}
|
|
|
|
timeline.record("passes");
|
2013-12-11 07:27:15 +00:00
|
|
|
}
|
|
|
|
|
2018-12-03 19:50:39 +00:00
|
|
|
Ok(LtoModuleCodegen::Fat {
|
2017-07-23 15:14:38 +00:00
|
|
|
module: Some(module),
|
|
|
|
_serialized_bitcode: serialized_bitcode,
|
2018-12-03 19:50:39 +00:00
|
|
|
})
|
2017-07-23 15:14:38 +00:00
|
|
|
}
|
|
|
|
|
2018-07-17 11:26:22 +00:00
|
|
|
struct Linker<'a>(&'a mut llvm::Linker<'a>);
|
2018-02-12 16:38:46 +00:00
|
|
|
|
2018-07-17 11:26:22 +00:00
|
|
|
impl Linker<'a> {
|
|
|
|
fn new(llmod: &'a llvm::Module) -> Self {
|
2018-02-12 16:38:46 +00:00
|
|
|
unsafe { Linker(llvm::LLVMRustLinkerNew(llmod)) }
|
|
|
|
}
|
|
|
|
|
|
|
|
fn add(&mut self, bytecode: &[u8]) -> Result<(), ()> {
|
|
|
|
unsafe {
|
|
|
|
if llvm::LLVMRustLinkerAdd(self.0,
|
|
|
|
bytecode.as_ptr() as *const libc::c_char,
|
|
|
|
bytecode.len()) {
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
Err(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-17 11:26:22 +00:00
|
|
|
impl Drop for Linker<'a> {
|
2018-02-12 16:38:46 +00:00
|
|
|
fn drop(&mut self) {
|
2018-07-17 11:26:22 +00:00
|
|
|
unsafe { llvm::LLVMRustLinkerFree(&mut *(self.0 as *mut _)); }
|
2018-02-12 16:38:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
/// Prepare "thin" LTO to get run on these modules.
|
|
|
|
///
|
|
|
|
/// The general structure of ThinLTO is quite different from the structure of
|
|
|
|
/// "fat" LTO above. With "fat" LTO all LLVM modules in question are merged into
|
|
|
|
/// one giant LLVM module, and then we run more optimization passes over this
|
|
|
|
/// big module after internalizing most symbols. Thin LTO, on the other hand,
|
|
|
|
/// avoid this large bottleneck through more targeted optimization.
|
|
|
|
///
|
|
|
|
/// At a high level Thin LTO looks like:
|
|
|
|
///
|
|
|
|
/// 1. Prepare a "summary" of each LLVM module in question which describes
|
|
|
|
/// the values inside, cost of the values, etc.
|
|
|
|
/// 2. Merge the summaries of all modules in question into one "index"
|
|
|
|
/// 3. Perform some global analysis on this index
|
|
|
|
/// 4. For each module, use the index and analysis calculated previously to
|
|
|
|
/// perform local transformations on the module, for example inlining
|
|
|
|
/// small functions from other modules.
|
|
|
|
/// 5. Run thin-specific optimization passes over each module, and then code
|
|
|
|
/// generate everything at the end.
|
|
|
|
///
|
|
|
|
/// The summary for each module is intended to be quite cheap, and the global
|
|
|
|
/// index is relatively quite cheap to create as well. As a result, the goal of
|
|
|
|
/// ThinLTO is to reduce the bottleneck on LTO and enable LTO to be used in more
|
|
|
|
/// situations. For example one cheap optimization is that we can parallelize
|
|
|
|
/// all codegen modules, easily making use of all the cores on a machine.
|
|
|
|
///
|
|
|
|
/// With all that in mind, the function here is designed at specifically just
|
|
|
|
/// calculating the *index* for ThinLTO. This index will then be shared amongst
|
2018-05-08 13:10:16 +00:00
|
|
|
/// all of the `LtoModuleCodegen` units returned below and destroyed once
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
/// they all go out of scope.
|
2018-10-23 15:01:35 +00:00
|
|
|
fn thin_lto(cgcx: &CodegenContext<LlvmCodegenBackend>,
|
2018-08-17 14:07:23 +00:00
|
|
|
diag_handler: &Handler,
|
2018-12-04 15:24:20 +00:00
|
|
|
modules: Vec<(String, ThinBuffer)>,
|
2018-10-23 15:01:35 +00:00
|
|
|
serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>,
|
|
|
|
cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
symbol_white_list: &[*const libc::c_char],
|
|
|
|
timeline: &mut Timeline)
|
2018-10-23 15:01:35 +00:00
|
|
|
-> Result<(Vec<LtoModuleCodegen<LlvmCodegenBackend>>, Vec<WorkProduct>), FatalError>
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
{
|
|
|
|
unsafe {
|
|
|
|
info!("going for that thin, thin LTO");
|
|
|
|
|
2018-08-31 13:18:08 +00:00
|
|
|
let green_modules: FxHashMap<_, _> = cached_modules
|
|
|
|
.iter()
|
|
|
|
.map(|&(_, ref wp)| (wp.cgu_name.clone(), wp.clone()))
|
|
|
|
.collect();
|
|
|
|
|
2018-10-06 09:45:11 +00:00
|
|
|
let full_scope_len = modules.len() + serialized_modules.len() + cached_modules.len();
|
|
|
|
let mut thin_buffers = Vec::with_capacity(modules.len());
|
|
|
|
let mut module_names = Vec::with_capacity(full_scope_len);
|
|
|
|
let mut thin_modules = Vec::with_capacity(full_scope_len);
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
|
2018-12-04 15:24:20 +00:00
|
|
|
for (i, (name, buffer)) in modules.into_iter().enumerate() {
|
|
|
|
info!("local module: {} - {}", i, name);
|
|
|
|
let cname = CString::new(name.clone()).unwrap();
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
thin_modules.push(llvm::ThinLTOModule {
|
2018-12-04 15:24:20 +00:00
|
|
|
identifier: cname.as_ptr(),
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
data: buffer.data().as_ptr(),
|
|
|
|
len: buffer.data().len(),
|
|
|
|
});
|
|
|
|
thin_buffers.push(buffer);
|
2018-12-04 15:24:20 +00:00
|
|
|
module_names.push(cname);
|
|
|
|
timeline.record(&name);
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: All upstream crates are deserialized internally in the
|
|
|
|
// function below to extract their summary and modules. Note that
|
|
|
|
// unlike the loop above we *must* decode and/or read something
|
|
|
|
// here as these are all just serialized files on disk. An
|
|
|
|
// improvement, however, to make here would be to store the
|
|
|
|
// module summary separately from the actual module itself. Right
|
|
|
|
// now this is store in one large bitcode file, and the entire
|
|
|
|
// file is deflate-compressed. We could try to bypass some of the
|
|
|
|
// decompression by storing the index uncompressed and only
|
|
|
|
// lazily decompressing the bytecode if necessary.
|
|
|
|
//
|
|
|
|
// Note that truly taking advantage of this optimization will
|
|
|
|
// likely be further down the road. We'd have to implement
|
|
|
|
// incremental ThinLTO first where we could actually avoid
|
|
|
|
// looking at upstream modules entirely sometimes (the contents,
|
|
|
|
// we must always unconditionally look at the index).
|
2018-10-06 09:45:11 +00:00
|
|
|
let mut serialized = Vec::with_capacity(serialized_modules.len() + cached_modules.len());
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
|
2018-08-31 13:18:08 +00:00
|
|
|
let cached_modules = cached_modules.into_iter().map(|(sm, wp)| {
|
|
|
|
(sm, CString::new(wp.cgu_name).unwrap())
|
|
|
|
});
|
|
|
|
|
|
|
|
for (module, name) in serialized_modules.into_iter().chain(cached_modules) {
|
|
|
|
info!("upstream or cached module {:?}", name);
|
2018-08-20 15:13:01 +00:00
|
|
|
thin_modules.push(llvm::ThinLTOModule {
|
|
|
|
identifier: name.as_ptr(),
|
|
|
|
data: module.data().as_ptr(),
|
|
|
|
len: module.data().len(),
|
|
|
|
});
|
|
|
|
serialized.push(module);
|
|
|
|
module_names.push(name);
|
|
|
|
}
|
|
|
|
|
2018-08-31 13:18:08 +00:00
|
|
|
// Sanity check
|
|
|
|
assert_eq!(thin_modules.len(), module_names.len());
|
|
|
|
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
// Delegate to the C++ bindings to create some data here. Once this is a
|
|
|
|
// tried-and-true interface we may wish to try to upstream some of this
|
|
|
|
// to LLVM itself, right now we reimplement a lot of what they do
|
|
|
|
// upstream...
|
|
|
|
let data = llvm::LLVMRustCreateThinLTOData(
|
|
|
|
thin_modules.as_ptr(),
|
|
|
|
thin_modules.len() as u32,
|
|
|
|
symbol_white_list.as_ptr(),
|
|
|
|
symbol_white_list.len() as u32,
|
2018-07-17 13:43:49 +00:00
|
|
|
).ok_or_else(|| {
|
2018-10-06 09:42:14 +00:00
|
|
|
write::llvm_err(&diag_handler, "failed to prepare thin LTO context")
|
2018-07-17 13:43:49 +00:00
|
|
|
})?;
|
|
|
|
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
info!("thin LTO data created");
|
|
|
|
timeline.record("data");
|
|
|
|
|
2018-09-03 10:42:27 +00:00
|
|
|
let import_map = if cgcx.incr_comp_session_dir.is_some() {
|
|
|
|
ThinLTOImports::from_thin_lto_data(data)
|
|
|
|
} else {
|
|
|
|
// If we don't compile incrementally, we don't need to load the
|
|
|
|
// import data from LLVM.
|
|
|
|
assert!(green_modules.is_empty());
|
2018-10-16 14:57:53 +00:00
|
|
|
ThinLTOImports::default()
|
2018-09-03 10:42:27 +00:00
|
|
|
};
|
|
|
|
info!("thin LTO import map loaded");
|
|
|
|
timeline.record("import-map-loaded");
|
|
|
|
|
|
|
|
let data = ThinData(data);
|
|
|
|
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
// Throw our data in an `Arc` as we'll be sharing it across threads. We
|
|
|
|
// also put all memory referenced by the C++ data (buffers, ids, etc)
|
|
|
|
// into the arc as well. After this we'll create a thin module
|
2018-05-08 13:10:16 +00:00
|
|
|
// codegen per module in this data.
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
let shared = Arc::new(ThinShared {
|
|
|
|
data,
|
|
|
|
thin_buffers,
|
|
|
|
serialized_modules: serialized,
|
|
|
|
module_names,
|
|
|
|
});
|
2018-08-31 13:18:08 +00:00
|
|
|
|
|
|
|
let mut copy_jobs = vec![];
|
|
|
|
let mut opt_jobs = vec![];
|
|
|
|
|
2018-09-03 10:42:27 +00:00
|
|
|
info!("checking which modules can be-reused and which have to be re-optimized.");
|
2018-08-31 13:18:08 +00:00
|
|
|
for (module_index, module_name) in shared.module_names.iter().enumerate() {
|
|
|
|
let module_name = module_name_to_str(module_name);
|
|
|
|
|
2018-09-03 10:42:27 +00:00
|
|
|
// If the module hasn't changed and none of the modules it imports
|
|
|
|
// from has changed, we can re-use the post-ThinLTO version of the
|
|
|
|
// module.
|
2018-08-31 13:18:08 +00:00
|
|
|
if green_modules.contains_key(module_name) {
|
2018-09-03 10:42:27 +00:00
|
|
|
let imports_all_green = import_map.modules_imported_by(module_name)
|
|
|
|
.iter()
|
|
|
|
.all(|imported_module| green_modules.contains_key(imported_module));
|
2018-08-31 13:18:08 +00:00
|
|
|
|
|
|
|
if imports_all_green {
|
|
|
|
let work_product = green_modules[module_name].clone();
|
|
|
|
copy_jobs.push(work_product);
|
2018-09-03 10:42:27 +00:00
|
|
|
info!(" - {}: re-used", module_name);
|
2018-09-18 14:33:24 +00:00
|
|
|
cgcx.cgu_reuse_tracker.set_actual_reuse(module_name,
|
|
|
|
CguReuse::PostLto);
|
2018-08-31 13:18:08 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-03 10:42:27 +00:00
|
|
|
info!(" - {}: re-compiled", module_name);
|
2018-08-31 13:18:08 +00:00
|
|
|
opt_jobs.push(LtoModuleCodegen::Thin(ThinModule {
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
shared: shared.clone(),
|
2018-08-31 13:18:08 +00:00
|
|
|
idx: module_index,
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok((opt_jobs, copy_jobs))
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-23 15:01:35 +00:00
|
|
|
pub(crate) fn run_pass_manager(cgcx: &CodegenContext<LlvmCodegenBackend>,
|
|
|
|
module: &ModuleCodegen<ModuleLlvm>,
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
config: &ModuleConfig,
|
|
|
|
thin: bool) {
|
Implement LTO
This commit implements LTO for rust leveraging LLVM's passes. What this means
is:
* When compiling an rlib, in addition to insdering foo.o into the archive, also
insert foo.bc (the LLVM bytecode) of the optimized module.
* When the compiler detects the -Z lto option, it will attempt to perform LTO on
a staticlib or binary output. The compiler will emit an error if a dylib or
rlib output is being generated.
* The actual act of performing LTO is as follows:
1. Force all upstream libraries to have an rlib version available.
2. Load the bytecode of each upstream library from the rlib.
3. Link all this bytecode into the current LLVM module (just using llvm
apis)
4. Run an internalization pass which internalizes all symbols except those
found reachable for the local crate of compilation.
5. Run the LLVM LTO pass manager over this entire module
6a. If assembling an archive, then add all upstream rlibs into the output
archive. This ignores all of the object/bitcode/metadata files rust
generated and placed inside the rlibs.
6b. If linking a binary, create copies of all upstream rlibs, remove the
rust-generated object-file, and then link everything as usual.
As I have explained in #10741, this process is excruciatingly slow, so this is
*not* turned on by default, and it is also why I have decided to hide it behind
a -Z flag for now. The good news is that the binary sizes are about as small as
they can be as a result of LTO, so it's definitely working.
Closes #10741
Closes #10740
2013-12-03 07:19:29 +00:00
|
|
|
// Now we have one massive module inside of llmod. Time to run the
|
|
|
|
// LTO-specific optimization passes that LLVM provides.
|
|
|
|
//
|
|
|
|
// This code is based off the code found in llvm's LTO code generator:
|
|
|
|
// tools/lto/LTOCodeGenerator.cpp
|
|
|
|
debug!("running the pass manager");
|
|
|
|
unsafe {
|
|
|
|
let pm = llvm::LLVMCreatePassManager();
|
2018-10-23 15:01:35 +00:00
|
|
|
llvm::LLVMRustAddAnalysisPasses(module.module_llvm.tm, pm, module.module_llvm.llmod());
|
2018-05-30 20:48:20 +00:00
|
|
|
|
2018-06-12 19:05:37 +00:00
|
|
|
if config.verify_llvm_ir {
|
2018-05-30 20:48:20 +00:00
|
|
|
let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr() as *const _);
|
2018-07-12 15:00:49 +00:00
|
|
|
llvm::LLVMRustAddPass(pm, pass.unwrap());
|
2018-05-30 20:48:20 +00:00
|
|
|
}
|
Implement LTO
This commit implements LTO for rust leveraging LLVM's passes. What this means
is:
* When compiling an rlib, in addition to insdering foo.o into the archive, also
insert foo.bc (the LLVM bytecode) of the optimized module.
* When the compiler detects the -Z lto option, it will attempt to perform LTO on
a staticlib or binary output. The compiler will emit an error if a dylib or
rlib output is being generated.
* The actual act of performing LTO is as follows:
1. Force all upstream libraries to have an rlib version available.
2. Load the bytecode of each upstream library from the rlib.
3. Link all this bytecode into the current LLVM module (just using llvm
apis)
4. Run an internalization pass which internalizes all symbols except those
found reachable for the local crate of compilation.
5. Run the LLVM LTO pass manager over this entire module
6a. If assembling an archive, then add all upstream rlibs into the output
archive. This ignores all of the object/bitcode/metadata files rust
generated and placed inside the rlibs.
6b. If linking a binary, create copies of all upstream rlibs, remove the
rust-generated object-file, and then link everything as usual.
As I have explained in #10741, this process is excruciatingly slow, so this is
*not* turned on by default, and it is also why I have decided to hide it behind
a -Z flag for now. The good news is that the binary sizes are about as small as
they can be as a result of LTO, so it's definitely working.
Closes #10741
Closes #10740
2013-12-03 07:19:29 +00:00
|
|
|
|
2017-10-11 18:19:59 +00:00
|
|
|
// When optimizing for LTO we don't actually pass in `-O0`, but we force
|
|
|
|
// it to always happen at least with `-O1`.
|
|
|
|
//
|
|
|
|
// With ThinLTO we mess around a lot with symbol visibility in a way
|
|
|
|
// that will actually cause linking failures if we optimize at O0 which
|
|
|
|
// notable is lacking in dead code elimination. To ensure we at least
|
|
|
|
// get some optimizations and correctly link we forcibly switch to `-O1`
|
|
|
|
// to get dead code elimination.
|
|
|
|
//
|
|
|
|
// Note that in general this shouldn't matter too much as you typically
|
|
|
|
// only turn on ThinLTO when you're compiling with optimizations
|
|
|
|
// otherwise.
|
2018-10-23 15:01:35 +00:00
|
|
|
let opt_level = config.opt_level.map(get_llvm_opt_level)
|
|
|
|
.unwrap_or(llvm::CodeGenOptLevel::None);
|
2017-10-11 18:19:59 +00:00
|
|
|
let opt_level = match opt_level {
|
|
|
|
llvm::CodeGenOptLevel::None => llvm::CodeGenOptLevel::Less,
|
|
|
|
level => level,
|
|
|
|
};
|
2018-10-23 15:01:35 +00:00
|
|
|
with_llvm_pmb(module.module_llvm.llmod(), config, opt_level, false, &mut |b| {
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
if thin {
|
2018-11-05 13:52:08 +00:00
|
|
|
llvm::LLVMRustPassManagerBuilderPopulateThinLTOPassManager(b, pm);
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
} else {
|
|
|
|
llvm::LLVMPassManagerBuilderPopulateLTOPassManager(b, pm,
|
|
|
|
/* Internalize = */ False,
|
|
|
|
/* RunInliner = */ True);
|
|
|
|
}
|
2015-07-22 23:22:51 +00:00
|
|
|
});
|
Implement LTO
This commit implements LTO for rust leveraging LLVM's passes. What this means
is:
* When compiling an rlib, in addition to insdering foo.o into the archive, also
insert foo.bc (the LLVM bytecode) of the optimized module.
* When the compiler detects the -Z lto option, it will attempt to perform LTO on
a staticlib or binary output. The compiler will emit an error if a dylib or
rlib output is being generated.
* The actual act of performing LTO is as follows:
1. Force all upstream libraries to have an rlib version available.
2. Load the bytecode of each upstream library from the rlib.
3. Link all this bytecode into the current LLVM module (just using llvm
apis)
4. Run an internalization pass which internalizes all symbols except those
found reachable for the local crate of compilation.
5. Run the LLVM LTO pass manager over this entire module
6a. If assembling an archive, then add all upstream rlibs into the output
archive. This ignores all of the object/bitcode/metadata files rust
generated and placed inside the rlibs.
6b. If linking a binary, create copies of all upstream rlibs, remove the
rust-generated object-file, and then link everything as usual.
As I have explained in #10741, this process is excruciatingly slow, so this is
*not* turned on by default, and it is also why I have decided to hide it behind
a -Z flag for now. The good news is that the binary sizes are about as small as
they can be as a result of LTO, so it's definitely working.
Closes #10741
Closes #10740
2013-12-03 07:19:29 +00:00
|
|
|
|
2018-11-02 12:22:48 +00:00
|
|
|
// We always generate bitcode through ThinLTOBuffers,
|
|
|
|
// which do not support anonymous globals
|
|
|
|
if config.bitcode_needed() {
|
|
|
|
let pass = llvm::LLVMRustFindAndCreatePass("name-anon-globals\0".as_ptr() as *const _);
|
|
|
|
llvm::LLVMRustAddPass(pm, pass.unwrap());
|
|
|
|
}
|
|
|
|
|
2018-06-12 19:05:37 +00:00
|
|
|
if config.verify_llvm_ir {
|
2018-05-30 20:48:20 +00:00
|
|
|
let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr() as *const _);
|
2018-07-12 15:00:49 +00:00
|
|
|
llvm::LLVMRustAddPass(pm, pass.unwrap());
|
2018-05-30 20:48:20 +00:00
|
|
|
}
|
Implement LTO
This commit implements LTO for rust leveraging LLVM's passes. What this means
is:
* When compiling an rlib, in addition to insdering foo.o into the archive, also
insert foo.bc (the LLVM bytecode) of the optimized module.
* When the compiler detects the -Z lto option, it will attempt to perform LTO on
a staticlib or binary output. The compiler will emit an error if a dylib or
rlib output is being generated.
* The actual act of performing LTO is as follows:
1. Force all upstream libraries to have an rlib version available.
2. Load the bytecode of each upstream library from the rlib.
3. Link all this bytecode into the current LLVM module (just using llvm
apis)
4. Run an internalization pass which internalizes all symbols except those
found reachable for the local crate of compilation.
5. Run the LLVM LTO pass manager over this entire module
6a. If assembling an archive, then add all upstream rlibs into the output
archive. This ignores all of the object/bitcode/metadata files rust
generated and placed inside the rlibs.
6b. If linking a binary, create copies of all upstream rlibs, remove the
rust-generated object-file, and then link everything as usual.
As I have explained in #10741, this process is excruciatingly slow, so this is
*not* turned on by default, and it is also why I have decided to hide it behind
a -Z flag for now. The good news is that the binary sizes are about as small as
they can be as a result of LTO, so it's definitely working.
Closes #10741
Closes #10740
2013-12-03 07:19:29 +00:00
|
|
|
|
2018-10-23 15:01:35 +00:00
|
|
|
time_ext(cgcx.time_passes, None, "LTO passes", ||
|
|
|
|
llvm::LLVMRunPassManager(pm, module.module_llvm.llmod()));
|
Implement LTO
This commit implements LTO for rust leveraging LLVM's passes. What this means
is:
* When compiling an rlib, in addition to insdering foo.o into the archive, also
insert foo.bc (the LLVM bytecode) of the optimized module.
* When the compiler detects the -Z lto option, it will attempt to perform LTO on
a staticlib or binary output. The compiler will emit an error if a dylib or
rlib output is being generated.
* The actual act of performing LTO is as follows:
1. Force all upstream libraries to have an rlib version available.
2. Load the bytecode of each upstream library from the rlib.
3. Link all this bytecode into the current LLVM module (just using llvm
apis)
4. Run an internalization pass which internalizes all symbols except those
found reachable for the local crate of compilation.
5. Run the LLVM LTO pass manager over this entire module
6a. If assembling an archive, then add all upstream rlibs into the output
archive. This ignores all of the object/bitcode/metadata files rust
generated and placed inside the rlibs.
6b. If linking a binary, create copies of all upstream rlibs, remove the
rust-generated object-file, and then link everything as usual.
As I have explained in #10741, this process is excruciatingly slow, so this is
*not* turned on by default, and it is also why I have decided to hide it behind
a -Z flag for now. The good news is that the binary sizes are about as small as
they can be as a result of LTO, so it's definitely working.
Closes #10741
Closes #10740
2013-12-03 07:19:29 +00:00
|
|
|
|
|
|
|
llvm::LLVMDisposePassManager(pm);
|
|
|
|
}
|
|
|
|
debug!("lto done");
|
|
|
|
}
|
2014-07-31 13:05:08 +00:00
|
|
|
|
2018-07-17 13:08:25 +00:00
|
|
|
pub struct ModuleBuffer(&'static mut llvm::ModuleBuffer);
|
2017-07-23 15:14:38 +00:00
|
|
|
|
|
|
|
unsafe impl Send for ModuleBuffer {}
|
|
|
|
unsafe impl Sync for ModuleBuffer {}
|
|
|
|
|
|
|
|
impl ModuleBuffer {
|
2018-06-27 14:57:25 +00:00
|
|
|
pub fn new(m: &llvm::Module) -> ModuleBuffer {
|
2017-07-23 15:14:38 +00:00
|
|
|
ModuleBuffer(unsafe {
|
|
|
|
llvm::LLVMRustModuleBufferCreate(m)
|
|
|
|
})
|
|
|
|
}
|
2018-10-23 15:01:35 +00:00
|
|
|
}
|
2017-07-23 15:14:38 +00:00
|
|
|
|
2018-10-23 15:01:35 +00:00
|
|
|
impl ModuleBufferMethods for ModuleBuffer {
|
|
|
|
fn data(&self) -> &[u8] {
|
2017-07-23 15:14:38 +00:00
|
|
|
unsafe {
|
|
|
|
let ptr = llvm::LLVMRustModuleBufferPtr(self.0);
|
|
|
|
let len = llvm::LLVMRustModuleBufferLen(self.0);
|
|
|
|
slice::from_raw_parts(ptr, len)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for ModuleBuffer {
|
|
|
|
fn drop(&mut self) {
|
2018-07-17 13:08:25 +00:00
|
|
|
unsafe { llvm::LLVMRustModuleBufferFree(&mut *(self.0 as *mut _)); }
|
2017-07-23 15:14:38 +00:00
|
|
|
}
|
2014-07-31 13:05:08 +00:00
|
|
|
}
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
|
2018-10-23 15:01:35 +00:00
|
|
|
pub struct ThinData(&'static mut llvm::ThinLTOData);
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
|
|
|
|
unsafe impl Send for ThinData {}
|
|
|
|
unsafe impl Sync for ThinData {}
|
|
|
|
|
|
|
|
impl Drop for ThinData {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
unsafe {
|
2018-07-17 13:43:49 +00:00
|
|
|
llvm::LLVMRustFreeThinLTOData(&mut *(self.0 as *mut _));
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-17 13:31:09 +00:00
|
|
|
pub struct ThinBuffer(&'static mut llvm::ThinLTOBuffer);
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
|
|
|
|
unsafe impl Send for ThinBuffer {}
|
|
|
|
unsafe impl Sync for ThinBuffer {}
|
|
|
|
|
|
|
|
impl ThinBuffer {
|
2018-06-27 14:57:25 +00:00
|
|
|
pub fn new(m: &llvm::Module) -> ThinBuffer {
|
2017-10-20 01:44:33 +00:00
|
|
|
unsafe {
|
|
|
|
let buffer = llvm::LLVMRustThinLTOBufferCreate(m);
|
|
|
|
ThinBuffer(buffer)
|
|
|
|
}
|
|
|
|
}
|
2018-10-23 15:01:35 +00:00
|
|
|
}
|
2017-10-20 01:44:33 +00:00
|
|
|
|
2018-10-23 15:01:35 +00:00
|
|
|
impl ThinBufferMethods for ThinBuffer {
|
|
|
|
fn data(&self) -> &[u8] {
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
unsafe {
|
|
|
|
let ptr = llvm::LLVMRustThinLTOBufferPtr(self.0) as *const _;
|
|
|
|
let len = llvm::LLVMRustThinLTOBufferLen(self.0);
|
|
|
|
slice::from_raw_parts(ptr, len)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for ThinBuffer {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
unsafe {
|
2018-07-17 13:31:09 +00:00
|
|
|
llvm::LLVMRustThinLTOBufferFree(&mut *(self.0 as *mut _));
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-23 15:01:35 +00:00
|
|
|
pub unsafe fn optimize_thin_module(
|
|
|
|
thin_module: &mut ThinModule<LlvmCodegenBackend>,
|
|
|
|
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
|
|
|
timeline: &mut Timeline
|
|
|
|
) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> {
|
|
|
|
let diag_handler = cgcx.create_diag_handler();
|
2018-11-16 11:39:56 +00:00
|
|
|
let tm = (cgcx.tm_factory.0)().map_err(|e| {
|
2018-10-23 15:01:35 +00:00
|
|
|
write::llvm_err(&diag_handler, &e)
|
|
|
|
})?;
|
|
|
|
|
|
|
|
// Right now the implementation we've got only works over serialized
|
|
|
|
// modules, so we create a fresh new LLVM context and parse the module
|
|
|
|
// into that context. One day, however, we may do this for upstream
|
|
|
|
// crates but for locally codegened modules we may be able to reuse
|
|
|
|
// that LLVM Context and Module.
|
|
|
|
let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names);
|
|
|
|
let llmod_raw = llvm::LLVMRustParseBitcodeForThinLTO(
|
|
|
|
llcx,
|
|
|
|
thin_module.data().as_ptr(),
|
|
|
|
thin_module.data().len(),
|
|
|
|
thin_module.shared.module_names[thin_module.idx].as_ptr(),
|
|
|
|
).ok_or_else(|| {
|
|
|
|
let msg = "failed to parse bitcode for thin LTO module";
|
|
|
|
write::llvm_err(&diag_handler, msg)
|
|
|
|
})? as *const _;
|
|
|
|
let module = ModuleCodegen {
|
|
|
|
module_llvm: ModuleLlvm {
|
|
|
|
llmod_raw,
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
llcx,
|
2018-10-23 15:01:35 +00:00
|
|
|
tm,
|
|
|
|
},
|
|
|
|
name: thin_module.name().to_string(),
|
|
|
|
kind: ModuleKind::Regular,
|
|
|
|
};
|
|
|
|
{
|
|
|
|
let llmod = module.module_llvm.llmod();
|
|
|
|
save_temp_bitcode(&cgcx, &module, "thin-lto-input");
|
|
|
|
|
|
|
|
// Before we do much else find the "main" `DICompileUnit` that we'll be
|
|
|
|
// using below. If we find more than one though then rustc has changed
|
|
|
|
// in a way we're not ready for, so generate an ICE by returning
|
|
|
|
// an error.
|
|
|
|
let mut cu1 = ptr::null_mut();
|
|
|
|
let mut cu2 = ptr::null_mut();
|
|
|
|
llvm::LLVMRustThinLTOGetDICompileUnit(llmod, &mut cu1, &mut cu2);
|
|
|
|
if !cu2.is_null() {
|
|
|
|
let msg = "multiple source DICompileUnits found";
|
|
|
|
return Err(write::llvm_err(&diag_handler, msg))
|
|
|
|
}
|
2017-12-16 16:20:54 +00:00
|
|
|
|
2018-10-23 15:01:35 +00:00
|
|
|
// Like with "fat" LTO, get some better optimizations if landing pads
|
|
|
|
// are disabled by removing all landing pads.
|
|
|
|
if cgcx.no_landing_pads {
|
|
|
|
llvm::LLVMRustMarkAllFunctionsNounwind(llmod);
|
|
|
|
save_temp_bitcode(&cgcx, &module, "thin-lto-after-nounwind");
|
|
|
|
timeline.record("nounwind");
|
|
|
|
}
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
|
2018-10-23 15:01:35 +00:00
|
|
|
// Up next comes the per-module local analyses that we do for Thin LTO.
|
|
|
|
// Each of these functions is basically copied from the LLVM
|
|
|
|
// implementation and then tailored to suit this implementation. Ideally
|
|
|
|
// each of these would be supported by upstream LLVM but that's perhaps
|
|
|
|
// a patch for another day!
|
|
|
|
//
|
|
|
|
// You can find some more comments about these functions in the LLVM
|
|
|
|
// bindings we've got (currently `PassWrapper.cpp`)
|
|
|
|
if !llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod) {
|
|
|
|
let msg = "failed to prepare thin LTO module";
|
|
|
|
return Err(write::llvm_err(&diag_handler, msg))
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
}
|
2018-10-23 15:01:35 +00:00
|
|
|
save_temp_bitcode(cgcx, &module, "thin-lto-after-rename");
|
|
|
|
timeline.record("rename");
|
|
|
|
if !llvm::LLVMRustPrepareThinLTOResolveWeak(thin_module.shared.data.0, llmod) {
|
|
|
|
let msg = "failed to prepare thin LTO module";
|
|
|
|
return Err(write::llvm_err(&diag_handler, msg))
|
|
|
|
}
|
|
|
|
save_temp_bitcode(cgcx, &module, "thin-lto-after-resolve");
|
|
|
|
timeline.record("resolve");
|
|
|
|
if !llvm::LLVMRustPrepareThinLTOInternalize(thin_module.shared.data.0, llmod) {
|
|
|
|
let msg = "failed to prepare thin LTO module";
|
|
|
|
return Err(write::llvm_err(&diag_handler, msg))
|
|
|
|
}
|
|
|
|
save_temp_bitcode(cgcx, &module, "thin-lto-after-internalize");
|
|
|
|
timeline.record("internalize");
|
|
|
|
if !llvm::LLVMRustPrepareThinLTOImport(thin_module.shared.data.0, llmod) {
|
|
|
|
let msg = "failed to prepare thin LTO module";
|
|
|
|
return Err(write::llvm_err(&diag_handler, msg))
|
|
|
|
}
|
|
|
|
save_temp_bitcode(cgcx, &module, "thin-lto-after-import");
|
|
|
|
timeline.record("import");
|
2017-12-21 15:03:16 +00:00
|
|
|
|
2018-10-23 15:01:35 +00:00
|
|
|
// Ok now this is a bit unfortunate. This is also something you won't
|
|
|
|
// find upstream in LLVM's ThinLTO passes! This is a hack for now to
|
|
|
|
// work around bugs in LLVM.
|
|
|
|
//
|
|
|
|
// First discovered in #45511 it was found that as part of ThinLTO
|
|
|
|
// importing passes LLVM will import `DICompileUnit` metadata
|
|
|
|
// information across modules. This means that we'll be working with one
|
|
|
|
// LLVM module that has multiple `DICompileUnit` instances in it (a
|
|
|
|
// bunch of `llvm.dbg.cu` members). Unfortunately there's a number of
|
|
|
|
// bugs in LLVM's backend which generates invalid DWARF in a situation
|
|
|
|
// like this:
|
|
|
|
//
|
|
|
|
// https://bugs.llvm.org/show_bug.cgi?id=35212
|
|
|
|
// https://bugs.llvm.org/show_bug.cgi?id=35562
|
|
|
|
//
|
|
|
|
// While the first bug there is fixed the second ended up causing #46346
|
|
|
|
// which was basically a resurgence of #45511 after LLVM's bug 35212 was
|
|
|
|
// fixed.
|
|
|
|
//
|
|
|
|
// This function below is a huge hack around this problem. The function
|
|
|
|
// below is defined in `PassWrapper.cpp` and will basically "merge"
|
|
|
|
// all `DICompileUnit` instances in a module. Basically it'll take all
|
|
|
|
// the objects, rewrite all pointers of `DISubprogram` to point to the
|
|
|
|
// first `DICompileUnit`, and then delete all the other units.
|
|
|
|
//
|
|
|
|
// This is probably mangling to the debug info slightly (but hopefully
|
|
|
|
// not too much) but for now at least gets LLVM to emit valid DWARF (or
|
|
|
|
// so it appears). Hopefully we can remove this once upstream bugs are
|
|
|
|
// fixed in LLVM.
|
|
|
|
llvm::LLVMRustThinLTOPatchDICompileUnit(llmod, cu1);
|
|
|
|
save_temp_bitcode(cgcx, &module, "thin-lto-after-patch");
|
|
|
|
timeline.record("patch");
|
|
|
|
|
|
|
|
// Alright now that we've done everything related to the ThinLTO
|
|
|
|
// analysis it's time to run some optimizations! Here we use the same
|
|
|
|
// `run_pass_manager` as the "fat" LTO above except that we tell it to
|
|
|
|
// populate a thin-specific pass manager, which presumably LLVM treats a
|
|
|
|
// little differently.
|
|
|
|
info!("running thin lto passes over {}", module.name);
|
|
|
|
let config = cgcx.config(module.kind);
|
|
|
|
run_pass_manager(cgcx, &module, config, true);
|
|
|
|
save_temp_bitcode(cgcx, &module, "thin-lto-after-pm");
|
|
|
|
timeline.record("thin-done");
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
}
|
2018-10-23 15:01:35 +00:00
|
|
|
Ok(module)
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
}
|
2018-08-17 14:07:23 +00:00
|
|
|
|
2018-10-16 14:57:53 +00:00
|
|
|
#[derive(Debug, Default)]
|
2018-08-17 14:07:23 +00:00
|
|
|
pub struct ThinLTOImports {
|
|
|
|
// key = llvm name of importing module, value = list of modules it imports from
|
|
|
|
imports: FxHashMap<String, Vec<String>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ThinLTOImports {
|
2018-09-03 10:42:27 +00:00
|
|
|
fn modules_imported_by(&self, llvm_module_name: &str) -> &[String] {
|
2018-08-20 15:13:01 +00:00
|
|
|
self.imports.get(llvm_module_name).map(|v| &v[..]).unwrap_or(&[])
|
|
|
|
}
|
|
|
|
|
2018-08-17 14:07:23 +00:00
|
|
|
/// Load the ThinLTO import map from ThinLTOData.
|
|
|
|
unsafe fn from_thin_lto_data(data: *const llvm::ThinLTOData) -> ThinLTOImports {
|
|
|
|
unsafe extern "C" fn imported_module_callback(payload: *mut libc::c_void,
|
|
|
|
importing_module_name: *const libc::c_char,
|
|
|
|
imported_module_name: *const libc::c_char) {
|
|
|
|
let map = &mut* (payload as *mut ThinLTOImports);
|
|
|
|
let importing_module_name = CStr::from_ptr(importing_module_name);
|
|
|
|
let importing_module_name = module_name_to_str(&importing_module_name);
|
|
|
|
let imported_module_name = CStr::from_ptr(imported_module_name);
|
|
|
|
let imported_module_name = module_name_to_str(&imported_module_name);
|
2018-08-31 13:18:08 +00:00
|
|
|
|
2018-08-17 14:07:23 +00:00
|
|
|
if !map.imports.contains_key(importing_module_name) {
|
|
|
|
map.imports.insert(importing_module_name.to_owned(), vec![]);
|
|
|
|
}
|
2018-08-20 15:13:01 +00:00
|
|
|
|
2018-08-17 14:07:23 +00:00
|
|
|
map.imports
|
|
|
|
.get_mut(importing_module_name)
|
|
|
|
.unwrap()
|
|
|
|
.push(imported_module_name.to_owned());
|
|
|
|
}
|
2018-10-16 14:57:53 +00:00
|
|
|
let mut map = ThinLTOImports::default();
|
2018-08-17 14:07:23 +00:00
|
|
|
llvm::LLVMRustGetThinLTOModuleImports(data,
|
|
|
|
imported_module_callback,
|
|
|
|
&mut map as *mut _ as *mut libc::c_void);
|
|
|
|
map
|
|
|
|
}
|
2018-08-31 13:18:08 +00:00
|
|
|
}
|
2018-08-17 14:07:23 +00:00
|
|
|
|
2018-08-31 13:18:08 +00:00
|
|
|
fn module_name_to_str(c_str: &CStr) -> &str {
|
2018-10-06 09:49:03 +00:00
|
|
|
c_str.to_str().unwrap_or_else(|e|
|
|
|
|
bug!("Encountered non-utf8 LLVM module name `{}`: {}", c_str.to_string_lossy(), e))
|
2018-09-03 10:42:27 +00:00
|
|
|
}
|