Skip to content

Commit 4a216a2

Browse files
Share inline(never) generics across crates
This reduces code sizes and better respects programmer intent when marking inline(never). Previously such a marking was essentially ignored for generic functions, as we'd still inline them in remote crates.
1 parent 39cb338 commit 4a216a2

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
@@ -1946,8 +1946,6 @@ impl<'tcx> TyCtxt<'tcx> {
19461946

19471947
#[inline]
19481948
pub fn local_crate_exports_generics(self) -> bool {
1949-
debug_assert!(self.sess.opts.share_generics());
1950-
19511949
self.crate_types().iter().any(|crate_type| {
19521950
match crate_type {
19531951
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 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 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)