Skip to content

Commit 1ca6777

Browse files
committed
Auto merge of rust-lang#101403 - bjorn3:dylib_lto, r=Mark-Simulacrum
Enable LTO for rustc_driver.so Alternative to rust-lang#97154 This enables LTO'ing dylibs behind a feature flag and uses this feature for compiling rustc_driver.so.
2 parents 9be2f35 + 565b7e0 commit 1ca6777

File tree

15 files changed

+136
-28
lines changed

15 files changed

+136
-28
lines changed

compiler/rustc_codegen_llvm/src/back/lto.rs

+18-13
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ pub const THIN_LTO_KEYS_INCR_COMP_FILE_NAME: &str = "thin-lto-past-keys.bin";
3232

3333
pub fn crate_type_allows_lto(crate_type: CrateType) -> bool {
3434
match crate_type {
35-
CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => true,
36-
CrateType::Dylib | CrateType::Rlib | CrateType::ProcMacro => false,
35+
CrateType::Executable | CrateType::Dylib | CrateType::Staticlib | CrateType::Cdylib => true,
36+
CrateType::Rlib | CrateType::ProcMacro => false,
3737
}
3838
}
3939

@@ -73,17 +73,6 @@ fn prepare_lto(
7373
// with either fat or thin LTO
7474
let mut upstream_modules = Vec::new();
7575
if cgcx.lto != Lto::ThinLocal {
76-
if cgcx.opts.cg.prefer_dynamic {
77-
diag_handler
78-
.struct_err("cannot prefer dynamic linking when performing LTO")
79-
.note(
80-
"only 'staticlib', 'bin', and 'cdylib' outputs are \
81-
supported with LTO",
82-
)
83-
.emit();
84-
return Err(FatalError);
85-
}
86-
8776
// Make sure we actually can run LTO
8877
for crate_type in cgcx.crate_types.iter() {
8978
if !crate_type_allows_lto(*crate_type) {
@@ -92,9 +81,25 @@ fn prepare_lto(
9281
static library outputs",
9382
);
9483
return Err(e);
84+
} else if *crate_type == CrateType::Dylib {
85+
if !cgcx.opts.unstable_opts.dylib_lto {
86+
return Err(diag_handler
87+
.fatal("lto cannot be used for `dylib` crate type without `-Zdylib-lto`"));
88+
}
9589
}
9690
}
9791

92+
if cgcx.opts.cg.prefer_dynamic && !cgcx.opts.unstable_opts.dylib_lto {
93+
diag_handler
94+
.struct_err("cannot prefer dynamic linking when performing LTO")
95+
.note(
96+
"only 'staticlib', 'bin', and 'cdylib' outputs are \
97+
supported with LTO",
98+
)
99+
.emit();
100+
return Err(FatalError);
101+
}
102+
98103
for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() {
99104
let exported_symbols =
100105
cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO");

compiler/rustc_codegen_ssa/src/back/link.rs

+25-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_metadata::find_native_static_library;
1111
use rustc_metadata::fs::{emit_metadata, METADATA_FILENAME};
1212
use rustc_middle::middle::dependency_format::Linkage;
1313
use rustc_middle::middle::exported_symbols::SymbolExportKind;
14-
use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip};
14+
use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Lto, Strip};
1515
use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, SplitDwarfKind};
1616
use rustc_session::cstore::DllImport;
1717
use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename};
@@ -39,6 +39,7 @@ use cc::windows_registry;
3939
use regex::Regex;
4040
use tempfile::Builder as TempFileBuilder;
4141

42+
use itertools::Itertools;
4243
use std::borrow::Borrow;
4344
use std::cell::OnceCell;
4445
use std::collections::BTreeSet;
@@ -208,11 +209,29 @@ pub fn link_binary<'a>(
208209
}
209210

210211
pub fn each_linked_rlib(
212+
sess: &Session,
211213
info: &CrateInfo,
212214
f: &mut dyn FnMut(CrateNum, &Path),
213215
) -> Result<(), errors::LinkRlibError> {
214216
let crates = info.used_crates.iter();
215217
let mut fmts = None;
218+
219+
let lto_active = matches!(sess.lto(), Lto::Fat | Lto::Thin);
220+
if lto_active {
221+
for combination in info.dependency_formats.iter().combinations(2) {
222+
let (ty1, list1) = &combination[0];
223+
let (ty2, list2) = &combination[1];
224+
if list1 != list2 {
225+
return Err(errors::LinkRlibError::IncompatibleDependencyFormats {
226+
ty1: format!("{ty1:?}"),
227+
ty2: format!("{ty2:?}"),
228+
list1: format!("{list1:?}"),
229+
list2: format!("{list2:?}"),
230+
});
231+
}
232+
}
233+
}
234+
216235
for (ty, list) in info.dependency_formats.iter() {
217236
match ty {
218237
CrateType::Executable
@@ -222,6 +241,10 @@ pub fn each_linked_rlib(
222241
fmts = Some(list);
223242
break;
224243
}
244+
CrateType::Dylib if lto_active => {
245+
fmts = Some(list);
246+
break;
247+
}
225248
_ => {}
226249
}
227250
}
@@ -490,7 +513,7 @@ fn link_staticlib<'a>(
490513
)?;
491514
let mut all_native_libs = vec![];
492515

493-
let res = each_linked_rlib(&codegen_results.crate_info, &mut |cnum, path| {
516+
let res = each_linked_rlib(sess, &codegen_results.crate_info, &mut |cnum, path| {
494517
let name = codegen_results.crate_info.crate_name[&cnum];
495518
let native_libs = &codegen_results.crate_info.native_libraries[&cnum];
496519

compiler/rustc_codegen_ssa/src/back/symbol_export.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_middle::ty::query::{ExternProviders, Providers};
1313
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
1414
use rustc_middle::ty::Instance;
1515
use rustc_middle::ty::{self, SymbolName, TyCtxt};
16-
use rustc_session::config::CrateType;
16+
use rustc_session::config::{CrateType, OomStrategy};
1717
use rustc_target::spec::SanitizerSet;
1818

1919
pub fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel {
@@ -206,6 +206,15 @@ fn exported_symbols_provider_local<'tcx>(
206206
},
207207
));
208208
}
209+
210+
symbols.push((
211+
ExportedSymbol::NoDefId(SymbolName::new(tcx, OomStrategy::SYMBOL)),
212+
SymbolExportInfo {
213+
level: SymbolExportLevel::Rust,
214+
kind: SymbolExportKind::Text,
215+
used: false,
216+
},
217+
));
209218
}
210219

211220
if tcx.sess.instrument_coverage() || tcx.sess.opts.cg.profile_generate.enabled() {

compiler/rustc_codegen_ssa/src/back/write.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -999,6 +999,14 @@ fn start_executing_work<B: ExtraBackendMethods>(
999999
let coordinator_send = tx_to_llvm_workers;
10001000
let sess = tcx.sess;
10011001

1002+
let mut each_linked_rlib_for_lto = Vec::new();
1003+
drop(link::each_linked_rlib(sess, crate_info, &mut |cnum, path| {
1004+
if link::ignored_for_lto(sess, crate_info, cnum) {
1005+
return;
1006+
}
1007+
each_linked_rlib_for_lto.push((cnum, path.to_path_buf()));
1008+
}));
1009+
10021010
// Compute the set of symbols we need to retain when doing LTO (if we need to)
10031011
let exported_symbols = {
10041012
let mut exported_symbols = FxHashMap::default();
@@ -1020,7 +1028,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
10201028
}
10211029
Lto::Fat | Lto::Thin => {
10221030
exported_symbols.insert(LOCAL_CRATE, copy_symbols(LOCAL_CRATE));
1023-
for &cnum in tcx.crates(()).iter() {
1031+
for &(cnum, ref _path) in &each_linked_rlib_for_lto {
10241032
exported_symbols.insert(cnum, copy_symbols(cnum));
10251033
}
10261034
Some(Arc::new(exported_symbols))
@@ -1040,14 +1048,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
10401048
})
10411049
.expect("failed to spawn helper thread");
10421050

1043-
let mut each_linked_rlib_for_lto = Vec::new();
1044-
drop(link::each_linked_rlib(crate_info, &mut |cnum, path| {
1045-
if link::ignored_for_lto(sess, crate_info, cnum) {
1046-
return;
1047-
}
1048-
each_linked_rlib_for_lto.push((cnum, path.to_path_buf()));
1049-
}));
1050-
10511051
let ol =
10521052
if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() {
10531053
// If we know that we won’t be doing codegen, create target machines without optimisation.

compiler/rustc_codegen_ssa/src/errors.rs

+3
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ pub enum LinkRlibError {
127127

128128
#[diag(codegen_ssa_rlib_not_found)]
129129
NotFound { crate_name: Symbol },
130+
131+
#[diag(codegen_ssa_rlib_incompatible_dependency_formats)]
132+
IncompatibleDependencyFormats { ty1: String, ty2: String, list1: String, list2: String },
130133
}
131134

132135
pub struct ThorinErrorWrapper(pub thorin::Error);

compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ codegen_ssa_rlib_only_rmeta_found = could not find rlib for: `{$crate_name}`, fo
3434
3535
codegen_ssa_rlib_not_found = could not find rlib for: `{$crate_name}`
3636
37+
codegen_ssa_rlib_incompatible_dependency_formats = `{$ty1}` and `{$ty2}` do not have equivalent dependency formats (`{$list1}` vs `{$list2}`)
38+
3739
codegen_ssa_linking_failed = linking with `{$linker_path}` failed: {$exit_status}
3840
3941
codegen_ssa_extern_funcs_not_found = some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified

compiler/rustc_interface/src/tests.rs

+1
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,7 @@ fn test_unstable_options_tracking_hash() {
648648
untracked!(dump_mir_dir, String::from("abc"));
649649
untracked!(dump_mir_exclude_pass_number, true);
650650
untracked!(dump_mir_graphviz, true);
651+
untracked!(dylib_lto, true);
651652
untracked!(emit_stack_sizes, true);
652653
untracked!(future_incompat_test, true);
653654
untracked!(hir_stats, true);

compiler/rustc_session/src/options.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1295,6 +1295,8 @@ options! {
12951295
an additional `.html` file showing the computed coverage spans."),
12961296
dwarf_version: Option<u32> = (None, parse_opt_number, [TRACKED],
12971297
"version of DWARF debug information to emit (default: 2 or 4, depending on platform)"),
1298+
dylib_lto: bool = (false, parse_bool, [UNTRACKED],
1299+
"enables LTO for dylib crate type"),
12981300
emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED],
12991301
"emit a section containing stack size metadata (default: no)"),
13001302
emit_thin_lto: bool = (true, parse_bool, [TRACKED],

config.toml.example

+5
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,11 @@ changelog-seen = 2
638638
# If an explicit setting is given, it will be used for all parts of the codebase.
639639
#new-symbol-mangling = true|false (see comment)
640640

641+
# Select LTO mode that will be used for compiling rustc. By default, thin local LTO
642+
# (LTO within a single crate) is used (like for any Rust crate). You can also select
643+
# "thin" or "fat" to apply Thin/Fat LTO to the `rustc_driver` dylib.
644+
#lto = "thin-local"
645+
641646
# =============================================================================
642647
# Options for specific targets
643648
#

src/bootstrap/compile.rs

+23-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use serde::Deserialize;
2121
use crate::builder::Cargo;
2222
use crate::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
2323
use crate::cache::{Interned, INTERNER};
24-
use crate::config::{LlvmLibunwind, TargetSelection};
24+
use crate::config::{LlvmLibunwind, RustcLto, TargetSelection};
2525
use crate::dist;
2626
use crate::native;
2727
use crate::tool::SourceType;
@@ -701,6 +701,28 @@ impl Step for Rustc {
701701
));
702702
}
703703

704+
// cfg(bootstrap): remove if condition once the bootstrap compiler supports dylib LTO
705+
if compiler.stage != 0 {
706+
match builder.config.rust_lto {
707+
RustcLto::Thin | RustcLto::Fat => {
708+
// Since using LTO for optimizing dylibs is currently experimental,
709+
// we need to pass -Zdylib-lto.
710+
cargo.rustflag("-Zdylib-lto");
711+
// Cargo by default passes `-Cembed-bitcode=no` and doesn't pass `-Clto` when
712+
// compiling dylibs (and their dependencies), even when LTO is enabled for the
713+
// crate. Therefore, we need to override `-Clto` and `-Cembed-bitcode` here.
714+
let lto_type = match builder.config.rust_lto {
715+
RustcLto::Thin => "thin",
716+
RustcLto::Fat => "fat",
717+
_ => unreachable!(),
718+
};
719+
cargo.rustflag(&format!("-Clto={}", lto_type));
720+
cargo.rustflag("-Cembed-bitcode=yes");
721+
}
722+
RustcLto::ThinLocal => { /* Do nothing, this is the default */ }
723+
}
724+
}
725+
704726
builder.info(&format!(
705727
"Building stage{} compiler artifacts ({} -> {})",
706728
compiler.stage, &compiler.host, target

src/bootstrap/config.rs

+30
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ pub struct Config {
158158
pub rust_new_symbol_mangling: Option<bool>,
159159
pub rust_profile_use: Option<String>,
160160
pub rust_profile_generate: Option<String>,
161+
pub rust_lto: RustcLto,
161162
pub llvm_profile_use: Option<String>,
162163
pub llvm_profile_generate: bool,
163164
pub llvm_libunwind_default: Option<LlvmLibunwind>,
@@ -319,6 +320,28 @@ impl SplitDebuginfo {
319320
}
320321
}
321322

323+
/// LTO mode used for compiling rustc itself.
324+
#[derive(Default, Clone)]
325+
pub enum RustcLto {
326+
#[default]
327+
ThinLocal,
328+
Thin,
329+
Fat,
330+
}
331+
332+
impl std::str::FromStr for RustcLto {
333+
type Err = String;
334+
335+
fn from_str(s: &str) -> Result<Self, Self::Err> {
336+
match s {
337+
"thin-local" => Ok(RustcLto::ThinLocal),
338+
"thin" => Ok(RustcLto::Thin),
339+
"fat" => Ok(RustcLto::Fat),
340+
_ => Err(format!("Invalid value for rustc LTO: {}", s)),
341+
}
342+
}
343+
}
344+
322345
#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
323346
pub struct TargetSelection {
324347
pub triple: Interned<String>,
@@ -726,6 +749,7 @@ define_config! {
726749
profile_use: Option<String> = "profile-use",
727750
// ignored; this is set from an env var set by bootstrap.py
728751
download_rustc: Option<StringOrBool> = "download-rustc",
752+
lto: Option<String> = "lto",
729753
}
730754
}
731755

@@ -1173,6 +1197,12 @@ impl Config {
11731197
config.rust_profile_use = flags.rust_profile_use.or(rust.profile_use);
11741198
config.rust_profile_generate = flags.rust_profile_generate.or(rust.profile_generate);
11751199
config.download_rustc_commit = download_ci_rustc_commit(&config, rust.download_rustc);
1200+
1201+
config.rust_lto = rust
1202+
.lto
1203+
.as_deref()
1204+
.map(|value| RustcLto::from_str(value).unwrap())
1205+
.unwrap_or_default();
11761206
} else {
11771207
config.rust_profile_use = flags.rust_profile_use;
11781208
config.rust_profile_generate = flags.rust_profile_generate;

src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile

+2-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ ENV RUST_CONFIGURE_ARGS \
7878
--set llvm.thin-lto=true \
7979
--set llvm.ninja=false \
8080
--set rust.jemalloc \
81-
--set rust.use-lld=true
81+
--set rust.use-lld=true \
82+
--set rust.lto=thin
8283
ENV SCRIPT ../src/ci/pgo.sh python3 ../x.py dist \
8384
--host $HOSTS --target $HOSTS \
8485
--include-default-paths \
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
## `dylib-lto`
2+
3+
This option enables using LTO for the `dylib` crate type. This is currently only used for compiling
4+
`rustc` itself (more specifically, the `librustc_driver` dylib).

src/test/rustdoc-ui/z-help.stdout

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
-Z dump-mir-graphviz=val -- in addition to `.mir` files, create graphviz `.dot` files (and with `-Z instrument-coverage`, also create a `.dot` file for the MIR-derived coverage graph) (default: no)
3737
-Z dump-mir-spanview=val -- in addition to `.mir` files, create `.html` files to view spans for all `statement`s (including terminators), only `terminator` spans, or computed `block` spans (one span encompassing a block's terminator and all statements). If `-Z instrument-coverage` is also enabled, create an additional `.html` file showing the computed coverage spans.
3838
-Z dwarf-version=val -- version of DWARF debug information to emit (default: 2 or 4, depending on platform)
39+
-Z dylib-lto=val -- enables LTO for dylib crate type
3940
-Z emit-stack-sizes=val -- emit a section containing stack size metadata (default: no)
4041
-Z emit-thin-lto=val -- emit the bc module with thin LTO info (default: yes)
4142
-Z export-executable-symbols=val -- export symbols from executables, as if they were dynamic libraries

0 commit comments

Comments
 (0)