Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 38d8997

Browse files
committed
Auto merge of rust-lang#123244 - Mark-Simulacrum:share-inline-never-generics, r=<try>
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 7bb0ef4 + 74af8ba commit 38d8997

File tree

18 files changed

+100
-26
lines changed

18 files changed

+100
-26
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4493,6 +4493,7 @@ dependencies = [
44934493
name = "rustc_monomorphize"
44944494
version = "0.0.0"
44954495
dependencies = [
4496+
"rustc_attr",
44964497
"rustc_data_structures",
44974498
"rustc_errors",
44984499
"rustc_fluent_macro",

compiler/rustc_codegen_llvm/src/callee.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,9 @@ pub fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) ->
113113
// This is a monomorphization. Its expected visibility depends
114114
// on whether we are in share-generics mode.
115115

116-
if cx.tcx.sess.opts.share_generics() {
116+
if cx.tcx.sess.opts.share_generics()
117+
|| tcx.codegen_fn_attrs(instance_def_id).inline == rustc_attr::InlineAttr::Never
118+
{
117119
// We are in share_generics mode.
118120

119121
if let Some(instance_def_id) = instance_def_id.as_local() {

compiler/rustc_codegen_ssa/src/back/symbol_export.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ fn exported_symbols_provider_local(
308308
));
309309
}
310310

311-
if tcx.sess.opts.share_generics() && tcx.local_crate_exports_generics() {
311+
if tcx.local_crate_exports_generics() {
312312
use rustc_middle::mir::mono::{Linkage, MonoItem, Visibility};
313313
use rustc_middle::ty::InstanceDef;
314314

@@ -336,6 +336,16 @@ fn exported_symbols_provider_local(
336336
continue;
337337
}
338338

339+
if !tcx.sess.opts.share_generics() {
340+
if tcx.codegen_fn_attrs(mono_item.def_id()).inline == rustc_attr::InlineAttr::Never
341+
{
342+
// this is OK, we explicitly allow sharing inline(never) across crates even
343+
// without share-generics.
344+
} else {
345+
continue;
346+
}
347+
}
348+
339349
match *mono_item {
340350
MonoItem::Fn(Instance { def: InstanceDef::Item(def), args }) => {
341351
if args.non_erasable_generics(tcx, def).next().is_some() {

compiler/rustc_middle/src/mir/mono.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,13 @@ impl<'tcx> MonoItem<'tcx> {
125125
return InstantiationMode::GloballyShared { may_conflict: false };
126126
}
127127

128+
if let InlineAttr::Never = tcx.codegen_fn_attrs(instance.def_id()).inline
129+
&& self.is_generic_fn(tcx)
130+
{
131+
// Upgrade inline(never) to a globally shared instance.
132+
return InstantiationMode::GloballyShared { may_conflict: true };
133+
}
134+
128135
// At this point we don't have explicit linkage and we're an
129136
// inlined function. If we're inlining into all CGUs then we'll
130137
// be creating a local copy per CGU.

compiler/rustc_middle/src/ty/context.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,8 +1503,6 @@ impl<'tcx> TyCtxt<'tcx> {
15031503

15041504
#[inline]
15051505
pub fn local_crate_exports_generics(self) -> bool {
1506-
debug_assert!(self.sess.opts.share_generics());
1507-
15081506
self.crate_types().iter().any(|crate_type| {
15091507
match crate_type {
15101508
CrateType::Executable

compiler/rustc_middle/src/ty/instance.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -194,19 +194,23 @@ impl<'tcx> Instance<'tcx> {
194194
/// This method already takes into account the global `-Zshare-generics`
195195
/// setting, always returning `None` if `share-generics` is off.
196196
pub fn upstream_monomorphization(&self, tcx: TyCtxt<'tcx>) -> Option<CrateNum> {
197-
// If we are not in share generics mode, we don't link to upstream
198-
// monomorphizations but always instantiate our own internal versions
199-
// instead.
200-
if !tcx.sess.opts.share_generics() {
201-
return None;
202-
}
203-
204197
// If this is an item that is defined in the local crate, no upstream
205198
// crate can know about it/provide a monomorphization.
206199
if self.def_id().is_local() {
207200
return None;
208201
}
209202

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

compiler/rustc_monomorphize/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ edition = "2021"
55

66
[dependencies]
77
# tidy-alphabetical-start
8+
rustc_attr = { path = "../rustc_attr" }
89
rustc_data_structures = { path = "../rustc_data_structures" }
910
rustc_errors = { path = "../rustc_errors" }
1011
rustc_fluent_macro = { path = "../rustc_fluent_macro" }

compiler/rustc_monomorphize/src/partitioning.rs

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,8 @@ where
206206
// available to downstream crates. This depends on whether we are in
207207
// share-generics mode and whether the current crate can even have
208208
// downstream crates.
209-
let export_generics =
210-
cx.tcx.sess.opts.share_generics() && cx.tcx.local_crate_exports_generics();
209+
let can_export_generics = cx.tcx.local_crate_exports_generics();
210+
let always_export_generics = can_export_generics && cx.tcx.sess.opts.share_generics();
211211

212212
let cgu_name_builder = &mut CodegenUnitNameBuilder::new(cx.tcx);
213213
let cgu_name_cache = &mut UnordMap::default();
@@ -247,7 +247,8 @@ where
247247
cx.tcx,
248248
&mono_item,
249249
&mut can_be_internalized,
250-
export_generics,
250+
can_export_generics,
251+
always_export_generics,
251252
);
252253
if visibility == Visibility::Hidden && can_be_internalized {
253254
internalization_candidates.insert(mono_item);
@@ -734,12 +735,19 @@ fn mono_item_linkage_and_visibility<'tcx>(
734735
tcx: TyCtxt<'tcx>,
735736
mono_item: &MonoItem<'tcx>,
736737
can_be_internalized: &mut bool,
737-
export_generics: bool,
738+
can_export_generics: bool,
739+
always_export_generics: bool,
738740
) -> (Linkage, Visibility) {
739741
if let Some(explicit_linkage) = mono_item.explicit_linkage(tcx) {
740742
return (explicit_linkage, Visibility::Default);
741743
}
742-
let vis = mono_item_visibility(tcx, mono_item, can_be_internalized, export_generics);
744+
let vis = mono_item_visibility(
745+
tcx,
746+
mono_item,
747+
can_be_internalized,
748+
can_export_generics,
749+
always_export_generics,
750+
);
743751
(Linkage::External, vis)
744752
}
745753

@@ -762,7 +770,8 @@ fn mono_item_visibility<'tcx>(
762770
tcx: TyCtxt<'tcx>,
763771
mono_item: &MonoItem<'tcx>,
764772
can_be_internalized: &mut bool,
765-
export_generics: bool,
773+
can_export_generics: bool,
774+
always_export_generics: bool,
766775
) -> Visibility {
767776
let instance = match mono_item {
768777
// This is pretty complicated; see below.
@@ -822,7 +831,11 @@ fn mono_item_visibility<'tcx>(
822831

823832
// Upstream `DefId` instances get different handling than local ones.
824833
let Some(def_id) = def_id.as_local() else {
825-
return if export_generics && is_generic {
834+
return if is_generic
835+
&& (always_export_generics
836+
|| (can_export_generics
837+
&& tcx.codegen_fn_attrs(def_id).inline == rustc_attr::InlineAttr::Never))
838+
{
826839
// If it is an upstream monomorphization and we export generics, we must make
827840
// it available to downstream crates.
828841
*can_be_internalized = false;
@@ -833,7 +846,12 @@ fn mono_item_visibility<'tcx>(
833846
};
834847

835848
if is_generic {
836-
if export_generics {
849+
// An inline(never) function won't get inlined in upstream crates anyway, so we might as
850+
// well export it.
851+
if always_export_generics
852+
|| (can_export_generics
853+
&& tcx.codegen_fn_attrs(def_id).inline == rustc_attr::InlineAttr::Never)
854+
{
837855
if tcx.is_unreachable_local_definition(def_id) {
838856
// This instance cannot be used from another crate.
839857
Visibility::Hidden

library/alloc/src/raw_vec.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,6 @@ impl<T, A: Allocator> RawVec<T, A> {
548548
// above `RawVec::grow_amortized` for details. (The `A` parameter isn't
549549
// significant, because the number of different `A` types seen in practice is
550550
// much smaller than the number of `T` types.)
551-
#[inline(never)]
552551
fn finish_grow<A>(
553552
new_layout: Result<Layout, LayoutError>,
554553
current_memory: Option<(NonNull<u8>, Layout)>,

library/alloc/src/vec/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1036,7 +1036,13 @@ impl<T, A: Allocator> Vec<T, A> {
10361036
/// ```
10371037
#[stable(feature = "try_reserve", since = "1.57.0")]
10381038
pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
1039-
self.buf.try_reserve(self.len, additional)
1039+
let len = self.len;
1040+
let ret = self.buf.try_reserve(len, additional);
1041+
unsafe {
1042+
// Inform the optimier that growing did not change the length.
1043+
core::hint::assert_unchecked(self.len == len);
1044+
}
1045+
ret
10401046
}
10411047

10421048
/// Tries to reserve the minimum capacity for at least `additional`

library/std/src/panicking.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,22 @@ pub const fn begin_panic<M: Any + Send>(msg: M) -> ! {
733733
}
734734
}
735735

736+
// This forces codegen of the function called by panic!() inside the std crate, rather than in
737+
// downstream crates. Primarily this is useful for rustc's codegen tests, which rely on noticing
738+
// complete removal of panic from generated IR. Since begin_panic is inline(never), it's only
739+
// codegen'd once per crate-graph so this pushes that to std rather than our codegen test crates.
740+
//
741+
// (See https://github.com/rust-lang/rust/pull/123244 for more info on why).
742+
//
743+
// If this is causing problems we can also modify those codegen tests to use a crate type like
744+
// cdylib which doesn't export "Rust" symbols to downstream linkage units.
745+
#[unstable(feature = "libstd_sys_internals", reason = "used by the panic! macro", issue = "none")]
746+
#[doc(hidden)]
747+
#[allow(dead_code)]
748+
#[used]
749+
pub static EMPTY_PANIC: fn(&'static str) -> ! =
750+
begin_panic::<&'static str> as fn(&'static str) -> !;
751+
736752
/// Central point for dispatching panics.
737753
///
738754
/// Executes the primary logic for a panic, including checking for recursive

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

Lines changed: 11 additions & 0 deletions
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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ pub extern "C" fn test() {
8585

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

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

tests/codegen/issues/issue-77812.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ extern "C" {
1515
fn exf2();
1616
}
1717

18+
#[no_mangle]
1819
pub static mut GLOBAL: Variant = Variant::Zero;
1920

2021
// CHECK-LABEL: @issue_77812
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
thread 'main' panicked at $DIR/issue-47429-short-backtraces.rs:23:5:
22
explicit panic
33
stack backtrace:
4-
0: std::panicking::begin_panic
4+
0: std::panicking::begin_panic::<&str>
55
1: issue_47429_short_backtraces::main
66
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
thread 'main' panicked at $DIR/runtime-switch.rs:26:5:
22
explicit panic
33
stack backtrace:
4-
0: std::panicking::begin_panic
4+
0: std::panicking::begin_panic::<&str>
55
1: runtime_switch::main
66
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
thread 'main' panicked at $DIR/short-ice-remove-middle-frames-2.rs:56:5:
22
debug!!!
33
stack backtrace:
4-
0: std::panicking::begin_panic
4+
0: std::panicking::begin_panic::<&str>
55
1: short_ice_remove_middle_frames_2::eight
66
2: short_ice_remove_middle_frames_2::seven::{{closure}}
77
[... omitted 3 frames ...]

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
thread 'main' panicked at $DIR/short-ice-remove-middle-frames.rs:52:5:
22
debug!!!
33
stack backtrace:
4-
0: std::panicking::begin_panic
4+
0: std::panicking::begin_panic::<&str>
55
1: short_ice_remove_middle_frames::seven
66
2: short_ice_remove_middle_frames::sixth
77
3: short_ice_remove_middle_frames::fifth::{{closure}}

0 commit comments

Comments
 (0)