Skip to content

Commit c5c8680

Browse files
committed
Introduce dedicated -Zdylib-lto flag for enabling LTO on dylibs
1 parent cba1681 commit c5c8680

File tree

11 files changed

+66
-13
lines changed

11 files changed

+66
-13
lines changed

compiler/rustc_codegen_llvm/src/back/lto.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,24 @@ fn prepare_lto(
8282
);
8383
return Err(e);
8484
} else if *crate_type == CrateType::Dylib {
85-
diag_handler.warn("LTO with dylibs may not be as effective");
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+
}
8689
}
8790
}
8891

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+
89103
for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() {
90104
let exported_symbols =
91105
cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO");

compiler/rustc_codegen_ssa/src/back/link.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -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;
@@ -49,7 +50,6 @@ use std::ops::Deref;
4950
use std::path::{Path, PathBuf};
5051
use std::process::{ExitStatus, Output, Stdio};
5152
use std::{env, fmt, fs, io, mem, str};
52-
use itertools::Itertools;
5353

5454
pub fn ensure_removed(diag_handler: &Handler, path: &Path) {
5555
if let Err(e) = fs::remove_file(path) {
@@ -219,10 +219,15 @@ pub fn each_linked_rlib(
219219
let lto_active = matches!(sess.lto(), Lto::Fat | Lto::Thin);
220220
if lto_active {
221221
for combination in info.dependency_formats.iter().combinations(2) {
222-
let (ty1, list1) = combination[0];
223-
let (ty2, list2) = combination[1];
222+
let (ty1, list1) = &combination[0];
223+
let (ty2, list2) = &combination[1];
224224
if list1 != list2 {
225-
return Err(format!("{ty1:?} and {ty2:?} do not have equivalent dependency formats (`{list1:?}` vs `{list2:?}`)"));
225+
return Err(errors::LinkRlibError::IncompatibleDependencyFormats {
226+
ty1: format!("{ty1:?}"),
227+
ty2: format!("{ty2:?}"),
228+
list1: format!("{list1:?}"),
229+
list2: format!("{list2:?}"),
230+
});
226231
}
227232
}
228233
}

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -638,10 +638,10 @@ 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 (LTO within a
642-
# single crate) is used. You can also select "thin" or "fat" to apply Thin/Fat LTO on the
643-
# `rustc_driver` dylib.
644-
#lto = thin-local
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"
645645

646646
# =============================================================================
647647
# Options for specific targets

src/bootstrap/compile.rs

+22
Original file line numberDiff line numberDiff line change
@@ -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

+3-4
Original file line numberDiff line numberDiff line change
@@ -321,12 +321,12 @@ impl SplitDebuginfo {
321321
}
322322

323323
/// LTO mode used for compiling rustc itself.
324-
#[derive(Default)]
324+
#[derive(Default, Clone)]
325325
pub enum RustcLto {
326326
#[default]
327327
ThinLocal,
328328
Thin,
329-
Fat
329+
Fat,
330330
}
331331

332332
impl std::str::FromStr for RustcLto {
@@ -1201,8 +1201,7 @@ impl Config {
12011201
config.rust_lto = rust
12021202
.lto
12031203
.as_deref()
1204-
.map(RustcLto::from_str)
1205-
.map(|v| v.expect("invalid value for rust.lto"))
1204+
.map(|value| RustcLto::from_str(value).unwrap())
12061205
.unwrap_or_default();
12071206
} else {
12081207
config.rust_profile_use = flags.rust_profile_use;
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)