Skip to content

Commit d53f0b1

Browse files
committed
Auto merge of rust-lang#123244 - Mark-Simulacrum:share-inline-never-generics, r=saethlin
Enable -Zshare-generics for inline(never) functions This avoids inlining cross-crate generic items when possible that are already marked inline(never), implying that the author is not intending for the function to be inlined by callers. As such, having a local copy may make it easier for LLVM to optimize but mostly just adds to binary bloat and codegen time. In practice our benchmarks indicate this is indeed a win for larger compilations, where the extra cost in dynamic linking to these symbols is diminished compared to the advantages in fewer copies that need optimizing in each binary. It might also make sense it expand this with other heuristics (e.g., `#[cold]`) in the future, but this seems like a good starting point. FWIW, I expect that doing cleanup in where we make the decision what should/shouldn't be shared is also a good idea. Way too much code needed to be tweaked to check this. But I'm hoping to leave that for a follow-up PR rather than blocking this on it.
2 parents a2545fd + 4a216a2 commit d53f0b1

26 files changed

+127
-52
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -4137,6 +4137,7 @@ name = "rustc_monomorphize"
41374137
version = "0.0.0"
41384138
dependencies = [
41394139
"rustc_abi",
4140+
"rustc_attr",
41404141
"rustc_data_structures",
41414142
"rustc_errors",
41424143
"rustc_fluent_macro",

compiler/rustc_codegen_llvm/src/callee.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,10 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t
104104

105105
let is_hidden = if is_generic {
106106
// This is a monomorphization of a generic function.
107-
if !cx.tcx.sess.opts.share_generics() {
107+
if !(cx.tcx.sess.opts.share_generics()
108+
|| tcx.codegen_fn_attrs(instance_def_id).inline
109+
== rustc_attr::InlineAttr::Never)
110+
{
108111
// When not sharing generics, all instances are in the same
109112
// crate and have hidden visibility.
110113
true

compiler/rustc_codegen_ssa/src/back/symbol_export.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ fn exported_symbols_provider_local(
282282
}));
283283
}
284284

285-
if tcx.sess.opts.share_generics() && tcx.local_crate_exports_generics() {
285+
if tcx.local_crate_exports_generics() {
286286
use rustc_middle::mir::mono::{Linkage, MonoItem, Visibility};
287287
use rustc_middle::ty::InstanceKind;
288288

@@ -310,6 +310,16 @@ fn exported_symbols_provider_local(
310310
continue;
311311
}
312312

313+
if !tcx.sess.opts.share_generics() {
314+
if tcx.codegen_fn_attrs(mono_item.def_id()).inline == rustc_attr::InlineAttr::Never
315+
{
316+
// this is OK, we explicitly allow sharing inline(never) across crates even
317+
// without share-generics.
318+
} else {
319+
continue;
320+
}
321+
}
322+
313323
match *mono_item {
314324
MonoItem::Fn(Instance { def: InstanceKind::Item(def), args }) => {
315325
if args.non_erasable_generics().next().is_some() {

compiler/rustc_middle/src/mir/mono.rs

+7
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,13 @@ impl<'tcx> MonoItem<'tcx> {
111111
return InstantiationMode::GloballyShared { may_conflict: false };
112112
}
113113

114+
if let InlineAttr::Never = tcx.codegen_fn_attrs(instance.def_id()).inline
115+
&& self.is_generic_fn()
116+
{
117+
// Upgrade inline(never) to a globally shared instance.
118+
return InstantiationMode::GloballyShared { may_conflict: true };
119+
}
120+
114121
// At this point we don't have explicit linkage and we're an
115122
// inlined function. If we're inlining into all CGUs then we'll
116123
// be creating a local copy per CGU.

compiler/rustc_middle/src/ty/context.rs

-2
Original file line numberDiff line numberDiff line change
@@ -1955,8 +1955,6 @@ impl<'tcx> TyCtxt<'tcx> {
19551955

19561956
#[inline]
19571957
pub fn local_crate_exports_generics(self) -> bool {
1958-
debug_assert!(self.sess.opts.share_generics());
1959-
19601958
self.crate_types().iter().any(|crate_type| {
19611959
match crate_type {
19621960
CrateType::Executable

compiler/rustc_middle/src/ty/instance.rs

+11-7
Original file line numberDiff line numberDiff line change
@@ -190,19 +190,23 @@ impl<'tcx> Instance<'tcx> {
190190
/// This method already takes into account the global `-Zshare-generics`
191191
/// setting, always returning `None` if `share-generics` is off.
192192
pub fn upstream_monomorphization(&self, tcx: TyCtxt<'tcx>) -> Option<CrateNum> {
193-
// If we are not in share generics mode, we don't link to upstream
194-
// monomorphizations but always instantiate our own internal versions
195-
// instead.
196-
if !tcx.sess.opts.share_generics() {
197-
return None;
198-
}
199-
200193
// If this is an item that is defined in the local crate, no upstream
201194
// crate can know about it/provide a monomorphization.
202195
if self.def_id().is_local() {
203196
return None;
204197
}
205198

199+
// If we are not in share generics mode, we don't link to upstream
200+
// monomorphizations but always instantiate our own internal versions
201+
// instead.
202+
if !tcx.sess.opts.share_generics()
203+
// However, if the def_id is marked inline(never), then it's fine to just reuse the
204+
// upstream monomorphization.
205+
&& tcx.codegen_fn_attrs(self.def_id()).inline != rustc_attr::InlineAttr::Never
206+
{
207+
return None;
208+
}
209+
206210
// If this a non-generic instance, it cannot be a shared monomorphization.
207211
self.args.non_erasable_generics().next()?;
208212

compiler/rustc_monomorphize/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ edition = "2021"
66
[dependencies]
77
# tidy-alphabetical-start
88
rustc_abi = { path = "../rustc_abi" }
9+
rustc_attr = { path = "../rustc_attr" }
910
rustc_data_structures = { path = "../rustc_data_structures" }
1011
rustc_errors = { path = "../rustc_errors" }
1112
rustc_fluent_macro = { path = "../rustc_fluent_macro" }

compiler/rustc_monomorphize/src/partitioning.rs

+24-8
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,8 @@ where
208208
// available to downstream crates. This depends on whether we are in
209209
// share-generics mode and whether the current crate can even have
210210
// downstream crates.
211-
let export_generics =
212-
cx.tcx.sess.opts.share_generics() && cx.tcx.local_crate_exports_generics();
211+
let can_export_generics = cx.tcx.local_crate_exports_generics();
212+
let always_export_generics = can_export_generics && cx.tcx.sess.opts.share_generics();
213213

214214
let cgu_name_builder = &mut CodegenUnitNameBuilder::new(cx.tcx);
215215
let cgu_name_cache = &mut UnordMap::default();
@@ -249,7 +249,8 @@ where
249249
cx.tcx,
250250
&mono_item,
251251
&mut can_be_internalized,
252-
export_generics,
252+
can_export_generics,
253+
always_export_generics,
253254
);
254255
if visibility == Visibility::Hidden && can_be_internalized {
255256
internalization_candidates.insert(mono_item);
@@ -739,12 +740,19 @@ fn mono_item_linkage_and_visibility<'tcx>(
739740
tcx: TyCtxt<'tcx>,
740741
mono_item: &MonoItem<'tcx>,
741742
can_be_internalized: &mut bool,
742-
export_generics: bool,
743+
can_export_generics: bool,
744+
always_export_generics: bool,
743745
) -> (Linkage, Visibility) {
744746
if let Some(explicit_linkage) = mono_item.explicit_linkage(tcx) {
745747
return (explicit_linkage, Visibility::Default);
746748
}
747-
let vis = mono_item_visibility(tcx, mono_item, can_be_internalized, export_generics);
749+
let vis = mono_item_visibility(
750+
tcx,
751+
mono_item,
752+
can_be_internalized,
753+
can_export_generics,
754+
always_export_generics,
755+
);
748756
(Linkage::External, vis)
749757
}
750758

@@ -767,7 +775,8 @@ fn mono_item_visibility<'tcx>(
767775
tcx: TyCtxt<'tcx>,
768776
mono_item: &MonoItem<'tcx>,
769777
can_be_internalized: &mut bool,
770-
export_generics: bool,
778+
can_export_generics: bool,
779+
always_export_generics: bool,
771780
) -> Visibility {
772781
let instance = match mono_item {
773782
// This is pretty complicated; see below.
@@ -826,7 +835,11 @@ fn mono_item_visibility<'tcx>(
826835

827836
// Upstream `DefId` instances get different handling than local ones.
828837
let Some(def_id) = def_id.as_local() else {
829-
return if export_generics && is_generic {
838+
return if is_generic
839+
&& (always_export_generics
840+
|| (can_export_generics
841+
&& tcx.codegen_fn_attrs(def_id).inline == rustc_attr::InlineAttr::Never))
842+
{
830843
// If it is an upstream monomorphization and we export generics, we must make
831844
// it available to downstream crates.
832845
*can_be_internalized = false;
@@ -837,7 +850,10 @@ fn mono_item_visibility<'tcx>(
837850
};
838851

839852
if is_generic {
840-
if export_generics {
853+
if always_export_generics
854+
|| (can_export_generics
855+
&& tcx.codegen_fn_attrs(def_id).inline == rustc_attr::InlineAttr::Never)
856+
{
841857
if tcx.is_unreachable_local_definition(def_id) {
842858
// This instance cannot be used from another crate.
843859
Visibility::Hidden

library/alloc/src/raw_vec.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -757,7 +757,9 @@ impl<A: Allocator> RawVecInner<A> {
757757
}
758758
}
759759

760-
#[inline(never)]
760+
// not marked inline(never) since we want optimizers to be able to observe the specifics of this
761+
// function, see tests/codegen/vec-reserve-extend.rs.
762+
#[cold]
761763
fn finish_grow<A>(
762764
new_layout: Layout,
763765
current_memory: Option<(NonNull<u8>, Layout)>,

library/std/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,7 @@
362362
#![feature(strict_provenance_atomic_ptr)]
363363
#![feature(sync_unsafe_cell)]
364364
#![feature(ub_checks)]
365+
#![feature(used_with_arg)]
365366
// tidy-alphabetical-end
366367
//
367368
// Library features (alloc):

library/std/src/panicking.rs

+16
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,22 @@ use crate::sys::backtrace;
2727
use crate::sys::stdio::panic_output;
2828
use crate::{fmt, intrinsics, process, thread};
2929

30+
// This forces codegen of the function called by panic!() inside the std crate, rather than in
31+
// downstream crates. Primarily this is useful for rustc's codegen tests, which rely on noticing
32+
// complete removal of panic from generated IR. Since begin_panic is inline(never), it's only
33+
// codegen'd once per crate-graph so this pushes that to std rather than our codegen test crates.
34+
//
35+
// (See https://github.com/rust-lang/rust/pull/123244 for more info on why).
36+
//
37+
// If this is causing problems we can also modify those codegen tests to use a crate type like
38+
// cdylib which doesn't export "Rust" symbols to downstream linkage units.
39+
#[unstable(feature = "libstd_sys_internals", reason = "used by the panic! macro", issue = "none")]
40+
#[doc(hidden)]
41+
#[allow(dead_code)]
42+
#[used(compiler)]
43+
pub static EMPTY_PANIC: fn(&'static str) -> ! =
44+
begin_panic::<&'static str> as fn(&'static str) -> !;
45+
3046
// Binary interface to the panic runtime that the standard library depends on.
3147
//
3248
// The standard library is tagged with `#![needs_panic_runtime]` (introduced in

tests/codegen-units/partitioning/auxiliary/cgu_generic_function.rs

+11
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,21 @@ pub fn foo<T>(x: T) -> (T, u32, i8) {
1111
#[inline(never)]
1212
fn bar<T>(x: T) -> (T, Struct) {
1313
let _ = not_exported_and_not_generic(0);
14+
exported_and_generic::<u32>(0);
1415
(x, Struct(1))
1516
}
1617

18+
pub static F: fn(u32) -> u32 = exported_and_generic::<u32>;
19+
1720
// These should not contribute to the codegen items of other crates.
21+
22+
// This is generic, but it's only instantiated with a u32 argument and that instantiation is present
23+
// in the local crate (see F above).
24+
#[inline(never)]
25+
pub fn exported_and_generic<T>(x: T) -> T {
26+
x
27+
}
28+
1829
#[inline(never)]
1930
pub fn exported_but_not_generic(x: i32) -> i64 {
2031
x as i64

tests/codegen/avr/avr-func-addrspace.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ pub extern "C" fn test() {
8686

8787
// A call through the Fn trait must use address space 1.
8888
//
89-
// CHECK: call{{.+}}addrspace(1) void @call_through_fn_trait()
89+
// CHECK: call{{.+}}addrspace(1) void @call_through_fn_trait({{.*}})
9090
call_through_fn_trait(&mut update_bar_value);
9191

9292
// A call through a global variable must use address space 1.

tests/codegen/issues/issue-13018.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
// A drop([...].clone()) sequence on an Rc should be a no-op
44
// In particular, no call to __rust_dealloc should be emitted
5-
#![crate_type = "lib"]
5+
//
6+
// We use a cdylib since it's a leaf unit for Rust purposes, so doesn't codegen -Zshare-generics
7+
// code.
8+
#![crate_type = "cdylib"]
69
use std::rc::Rc;
710

811
pub fn foo(t: &Rc<Vec<usize>>) {

tests/run-make/naked-symbol-visibility/a_rust_dylib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![feature(naked_functions, asm_const, linkage)]
1+
#![feature(naked_functions, linkage)]
22
#![crate_type = "dylib"]
33

44
use std::arch::naked_asm;
@@ -38,7 +38,7 @@ pub extern "C" fn public_vanilla() -> u32 {
3838

3939
#[naked]
4040
#[no_mangle]
41-
pub extern "C" fn public_naked() -> u32 {
41+
pub extern "C" fn public_naked_nongeneric() -> u32 {
4242
unsafe { naked_asm!("mov rax, 42", "ret") }
4343
}
4444

tests/run-make/naked-symbol-visibility/rmake.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@ fn main() {
1717
not_exported(&rdylib, "private_naked");
1818

1919
global_function(&rdylib, "public_vanilla");
20-
global_function(&rdylib, "public_naked");
20+
global_function(&rdylib, "public_naked_nongeneric");
2121

2222
not_exported(&rdylib, "public_vanilla_generic");
23-
not_exported(&rdylib, "public_naked_generic");
23+
// #[naked] functions are implicitly #[inline(never)], so they get shared regardless of
24+
// -Zshare-generics.
25+
global_function(&rdylib, "public_naked_generic");
2426

2527
global_function(&rdylib, "vanilla_external_linkage");
2628
global_function(&rdylib, "naked_external_linkage");

tests/ui/panics/issue-47429-short-backtraces.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
//@ check-run-results
77
//@ exec-env:RUST_BACKTRACE=1
88

9+
// This is needed to avoid test output differences across std being built with v0 symbols vs legacy
10+
// symbols.
11+
//@ normalize-stderr-test: "begin_panic::<&str>" -> "begin_panic"
12+
// And this is for differences between std with and without debuginfo.
13+
//@ normalize-stderr-test: "\n +at [^\n]+" -> ""
14+
915
//@ ignore-msvc see #62897 and `backtrace-debuginfo.rs` test
1016
//@ ignore-android FIXME #17520
1117
//@ ignore-openbsd no support for libbacktrace without filename
@@ -14,11 +20,6 @@
1420
//@ ignore-sgx no subprocess support
1521
//@ ignore-fuchsia Backtraces not symbolized
1622

17-
// NOTE(eddyb) output differs between symbol mangling schemes
18-
//@ revisions: legacy v0
19-
//@ [legacy] compile-flags: -Zunstable-options -Csymbol-mangling-version=legacy
20-
//@ [v0] compile-flags: -Csymbol-mangling-version=v0
21-
2223
fn main() {
2324
panic!()
2425
}

tests/ui/panics/issue-47429-short-backtraces.legacy.run.stderr renamed to tests/ui/panics/issue-47429-short-backtraces.run.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
thread 'main' panicked at $DIR/issue-47429-short-backtraces.rs:23:5:
1+
thread 'main' panicked at $DIR/issue-47429-short-backtraces.rs:24:5:
22
explicit panic
33
stack backtrace:
44
0: std::panicking::begin_panic

tests/ui/panics/issue-47429-short-backtraces.v0.run.stderr

-6
This file was deleted.

tests/ui/panics/runtime-switch.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
//@ check-run-results
77
//@ exec-env:RUST_BACKTRACE=0
88

9+
// This is needed to avoid test output differences across std being built with v0 symbols vs legacy
10+
// symbols.
11+
//@ normalize-stderr-test: "begin_panic::<&str>" -> "begin_panic"
12+
// And this is for differences between std with and without debuginfo.
13+
//@ normalize-stderr-test: "\n +at [^\n]+" -> ""
14+
915
//@ ignore-msvc see #62897 and `backtrace-debuginfo.rs` test
1016
//@ ignore-android FIXME #17520
1117
//@ ignore-openbsd no support for libbacktrace without filename
@@ -14,11 +20,6 @@
1420
//@ ignore-sgx no subprocess support
1521
//@ ignore-fuchsia Backtrace not symbolized
1622

17-
// NOTE(eddyb) output differs between symbol mangling schemes
18-
//@ revisions: legacy v0
19-
//@ [legacy] compile-flags: -Zunstable-options -Csymbol-mangling-version=legacy
20-
//@ [v0] compile-flags: -Csymbol-mangling-version=v0
21-
2223
#![feature(panic_backtrace_config)]
2324

2425
fn main() {

tests/ui/panics/runtime-switch.legacy.run.stderr renamed to tests/ui/panics/runtime-switch.run.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
thread 'main' panicked at $DIR/runtime-switch.rs:26:5:
1+
thread 'main' panicked at $DIR/runtime-switch.rs:27:5:
22
explicit panic
33
stack backtrace:
44
0: std::panicking::begin_panic

tests/ui/panics/runtime-switch.v0.run.stderr

-6
This file was deleted.

tests/ui/panics/short-ice-remove-middle-frames-2.rs

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
//@ ignore-sgx Backtraces not symbolized
1010
//@ ignore-fuchsia Backtraces not symbolized
1111
//@ ignore-msvc the `__rust_{begin,end}_short_backtrace` symbols aren't reliable.
12+
// This is needed to avoid test output differences across std being built with v0 symbols vs legacy
13+
// symbols.
14+
//@ normalize-stderr-test: "begin_panic::<&str>" -> "begin_panic"
15+
// And this is for differences between std with and without debuginfo.
16+
//@ normalize-stderr-test: "\n +at [^\n]+" -> ""
1217

1318
/// This test case make sure that we can have multiple pairs of `__rust_{begin,end}_short_backtrace`
1419

tests/ui/panics/short-ice-remove-middle-frames-2.run.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
thread 'main' panicked at $DIR/short-ice-remove-middle-frames-2.rs:56:5:
1+
thread 'main' panicked at $DIR/short-ice-remove-middle-frames-2.rs:61:5:
22
debug!!!
33
stack backtrace:
44
0: std::panicking::begin_panic

0 commit comments

Comments
 (0)