Skip to content

Commit 1f2a5ec

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 theory, TBD on in practice). It might also make sense it expand this with other heuristics (e.g., #[cold]). 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. r? `@Mark-Simulacrum` for perf at first
2 parents 70714e3 + 5702d83 commit 1f2a5ec

File tree

9 files changed

+71
-19
lines changed

9 files changed

+71
-19
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -4361,6 +4361,7 @@ dependencies = [
43614361
name = "rustc_monomorphize"
43624362
version = "0.0.0"
43634363
dependencies = [
4364+
"rustc_attr",
43644365
"rustc_data_structures",
43654366
"rustc_errors",
43664367
"rustc_fluent_macro",

compiler/rustc_codegen_llvm/src/callee.rs

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

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

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

compiler/rustc_codegen_ssa/src/back/symbol_export.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ fn exported_symbols_provider_local(
306306
));
307307
}
308308

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

@@ -334,6 +334,16 @@ fn exported_symbols_provider_local(
334334
continue;
335335
}
336336

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

compiler/rustc_middle/src/mir/mono.rs

+7
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,13 @@ impl<'tcx> MonoItem<'tcx> {
119119
return InstantiationMode::GloballyShared { may_conflict: false };
120120
}
121121

122+
if let InlineAttr::Never = tcx.codegen_fn_attrs(instance.def_id()).inline
123+
&& self.is_generic_fn(tcx)
124+
{
125+
// Upgrade inline(never) to a globally shared instance.
126+
return InstantiationMode::GloballyShared { may_conflict: true };
127+
}
128+
122129
// At this point we don't have explicit linkage and we're an
123130
// inlined function. If we're inlining into all CGUs then we'll
124131
// be creating a local copy per CGU.

compiler/rustc_middle/src/ty/context.rs

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

13021302
#[inline]
13031303
pub fn local_crate_exports_generics(self) -> bool {
1304-
debug_assert!(self.sess.opts.share_generics());
1305-
13061304
self.crate_types().iter().any(|crate_type| {
13071305
match crate_type {
13081306
CrateType::Executable

compiler/rustc_middle/src/ty/instance.rs

+11-7
Original file line numberDiff line numberDiff line change
@@ -157,19 +157,23 @@ impl<'tcx> Instance<'tcx> {
157157
/// This method already takes into account the global `-Zshare-generics`
158158
/// setting, always returning `None` if `share-generics` is off.
159159
pub fn upstream_monomorphization(&self, tcx: TyCtxt<'tcx>) -> Option<CrateNum> {
160-
// If we are not in share generics mode, we don't link to upstream
161-
// monomorphizations but always instantiate our own internal versions
162-
// instead.
163-
if !tcx.sess.opts.share_generics() {
164-
return None;
165-
}
166-
167160
// If this is an item that is defined in the local crate, no upstream
168161
// crate can know about it/provide a monomorphization.
169162
if self.def_id().is_local() {
170163
return None;
171164
}
172165

166+
// If we are not in share generics mode, we don't link to upstream
167+
// monomorphizations but always instantiate our own internal versions
168+
// instead.
169+
if !tcx.sess.opts.share_generics()
170+
// However, if the def_id is marked inline(never), then it's fine to just reuse the
171+
// upstream monomorphization.
172+
&& tcx.codegen_fn_attrs(self.def_id()).inline != rustc_attr::InlineAttr::Never
173+
{
174+
return None;
175+
}
176+
173177
// If this a non-generic instance, it cannot be a shared monomorphization.
174178
self.args.non_erasable_generics(tcx, self.def_id()).next()?;
175179

compiler/rustc_monomorphize/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ rustc_data_structures = { path = "../rustc_data_structures" }
99
rustc_errors = { path = "../rustc_errors" }
1010
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
1111
rustc_hir = { path = "../rustc_hir" }
12+
rustc_attr = { path = "../rustc_attr" }
1213
rustc_macros = { path = "../rustc_macros" }
1314
rustc_middle = { path = "../rustc_middle" }
1415
rustc_session = { path = "../rustc_session" }

compiler/rustc_monomorphize/src/partitioning.rs

+26-8
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,8 @@ where
203203
// available to downstream crates. This depends on whether we are in
204204
// share-generics mode and whether the current crate can even have
205205
// downstream crates.
206-
let export_generics =
207-
cx.tcx.sess.opts.share_generics() && cx.tcx.local_crate_exports_generics();
206+
let can_export_generics = cx.tcx.local_crate_exports_generics();
207+
let always_export_generics = can_export_generics && cx.tcx.sess.opts.share_generics();
208208

209209
let cgu_name_builder = &mut CodegenUnitNameBuilder::new(cx.tcx);
210210
let cgu_name_cache = &mut FxHashMap::default();
@@ -244,7 +244,8 @@ where
244244
cx.tcx,
245245
&mono_item,
246246
&mut can_be_internalized,
247-
export_generics,
247+
can_export_generics,
248+
always_export_generics,
248249
);
249250
if visibility == Visibility::Hidden && can_be_internalized {
250251
internalization_candidates.insert(mono_item);
@@ -727,12 +728,19 @@ fn mono_item_linkage_and_visibility<'tcx>(
727728
tcx: TyCtxt<'tcx>,
728729
mono_item: &MonoItem<'tcx>,
729730
can_be_internalized: &mut bool,
730-
export_generics: bool,
731+
can_export_generics: bool,
732+
always_export_generics: bool,
731733
) -> (Linkage, Visibility) {
732734
if let Some(explicit_linkage) = mono_item.explicit_linkage(tcx) {
733735
return (explicit_linkage, Visibility::Default);
734736
}
735-
let vis = mono_item_visibility(tcx, mono_item, can_be_internalized, export_generics);
737+
let vis = mono_item_visibility(
738+
tcx,
739+
mono_item,
740+
can_be_internalized,
741+
can_export_generics,
742+
always_export_generics,
743+
);
736744
(Linkage::External, vis)
737745
}
738746

@@ -755,7 +763,8 @@ fn mono_item_visibility<'tcx>(
755763
tcx: TyCtxt<'tcx>,
756764
mono_item: &MonoItem<'tcx>,
757765
can_be_internalized: &mut bool,
758-
export_generics: bool,
766+
can_export_generics: bool,
767+
always_export_generics: bool,
759768
) -> Visibility {
760769
let instance = match mono_item {
761770
// This is pretty complicated; see below.
@@ -812,7 +821,11 @@ fn mono_item_visibility<'tcx>(
812821

813822
// Upstream `DefId` instances get different handling than local ones.
814823
let Some(def_id) = def_id.as_local() else {
815-
return if export_generics && is_generic {
824+
return if is_generic
825+
&& (always_export_generics
826+
|| (can_export_generics
827+
&& tcx.codegen_fn_attrs(def_id).inline == rustc_attr::InlineAttr::Never))
828+
{
816829
// If it is an upstream monomorphization and we export generics, we must make
817830
// it available to downstream crates.
818831
*can_be_internalized = false;
@@ -823,7 +836,12 @@ fn mono_item_visibility<'tcx>(
823836
};
824837

825838
if is_generic {
826-
if export_generics {
839+
// An inline(never) function won't get inlined in upstream crates anyway, so we might as
840+
// well export it.
841+
if always_export_generics
842+
|| (can_export_generics
843+
&& tcx.codegen_fn_attrs(def_id).inline == rustc_attr::InlineAttr::Never)
844+
{
827845
if tcx.is_unreachable_local_definition(def_id) {
828846
// This instance cannot be used from another crate.
829847
Visibility::Hidden

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

0 commit comments

Comments
 (0)