Skip to content

Commit c0bb3b9

Browse files
committed
Auto merge of #143290 - azhogin:azhogin/link-pub-async-impls, r=oli-obk
pub async fn impl is monomorphized when func itself is monomorphized Implentation coroutine (`func::{closure#0}`) is monomorphized, when func itself is monomorphized. Currently, when `pub async fn foo(..)` is exported from lib and used in several dependent crates, only 'header' function is monomorphized in the defining crate. 'header' function, returning coroutine object, is monomorphized, but the coroutine's poll function (which actually implements all the logic for the function) is not. In such situation, `func::{closure#0}` will be monomorphized in every dependency. This PR adds monomorphization for `func::{closure#0}` (coroutine poll function), when func itself is monomorphized. Simple test with one lib async function and ten dependent crates (executable) that use the function, shows 5-7% compilation time improvement (single-threaded).
2 parents 84a1747 + c2c58cb commit c0bb3b9

File tree

13 files changed

+245
-15
lines changed

13 files changed

+245
-15
lines changed

compiler/rustc_monomorphize/src/collector.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1535,7 +1535,20 @@ impl<'v> RootCollector<'_, 'v> {
15351535
fn process_nested_body(&mut self, def_id: LocalDefId) {
15361536
match self.tcx.def_kind(def_id) {
15371537
DefKind::Closure => {
1538-
if self.strategy == MonoItemCollectionStrategy::Eager
1538+
// for 'pub async fn foo(..)' also trying to monomorphize foo::{closure}
1539+
let is_pub_fn_coroutine =
1540+
match *self.tcx.type_of(def_id).instantiate_identity().kind() {
1541+
ty::Coroutine(cor_id, _args) => {
1542+
let tcx = self.tcx;
1543+
let parent_id = tcx.parent(cor_id);
1544+
tcx.def_kind(parent_id) == DefKind::Fn
1545+
&& tcx.asyncness(parent_id).is_async()
1546+
&& tcx.visibility(parent_id).is_public()
1547+
}
1548+
ty::Closure(..) | ty::CoroutineClosure(..) => false,
1549+
_ => unreachable!(),
1550+
};
1551+
if (self.strategy == MonoItemCollectionStrategy::Eager || is_pub_fn_coroutine)
15391552
&& !self
15401553
.tcx
15411554
.generics_of(self.tcx.typeck_root_def_id(def_id.to_def_id()))

tests/codegen-llvm/async-fn-debug-awaitee-field.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ pub async fn async_fn_test() {
1818

1919
pub async fn foo() {}
2020

21+
// NONMSVC: [[AWAITEE_TYPE:![0-9]*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}", scope: [[AWAITEE_SCOPE:![0-9]*]],
22+
// MSVC: [[AWAITEE_TYPE:![0-9]*]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum2$<async_fn_debug_awaitee_field::foo::async_fn_env$0>",
23+
// NONMSVC: [[AWAITEE_SCOPE]] = !DINamespace(name: "foo",
2124
// NONMSVC: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}", scope: [[GEN_SCOPE:![0-9]*]],
2225
// MSVC: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum2$<async_fn_debug_awaitee_field::async_fn_test::async_fn_env$0>",
2326
// NONMSVC: [[GEN_SCOPE:!.*]] = !DINamespace(name: "async_fn_test",
2427
// CHECK: [[SUSPEND_STRUCT:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend0", scope: [[GEN]],
25-
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__awaitee", scope: [[SUSPEND_STRUCT]], {{.*}}, baseType: [[AWAITEE_TYPE:![0-9]*]],
26-
// NONMSVC: [[AWAITEE_TYPE]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}", scope: [[AWAITEE_SCOPE:![0-9]*]],
27-
// MSVC: [[AWAITEE_TYPE]] = !DICompositeType(tag: DW_TAG_union_type, name: "enum2$<async_fn_debug_awaitee_field::foo::async_fn_env$0>",
28-
// NONMSVC: [[AWAITEE_SCOPE]] = !DINamespace(name: "foo",
28+
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__awaitee", scope: [[SUSPEND_STRUCT]], {{.*}}, baseType: [[AWAITEE_TYPE]],
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//@ edition: 2024
2+
// When pub async fn is monomorphized, its implementation coroutine is also monomorphized
3+
//@ compile-flags: --crate-type=lib
4+
5+
//~ MONO_ITEM fn async_fn @@
6+
//~ MONO_ITEM fn async_fn::{closure#0} @@
7+
#[unsafe(no_mangle)]
8+
pub async fn async_fn(x: u64) -> bool {
9+
true
10+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
//@ only-x86_64-unknown-linux-gnu
2+
//@ compile-flags: -C panic=abort -Zinline-mir=no -Copt-level=0 -Zcross-crate-inline-threshold=never -Zmir-opt-level=0 -Cno-prepopulate-passes
3+
//@ no-prefer-dynamic
4+
//@ edition:2024
5+
#![crate_type = "lib"]
6+
7+
trait TestTrait {
8+
fn test_func(&self);
9+
}
10+
11+
struct TestStruct {}
12+
13+
impl TestTrait for TestStruct {
14+
fn test_func(&self) {
15+
println!("TestStruct::test_func");
16+
}
17+
}
18+
19+
#[inline(never)]
20+
pub fn foo() -> impl TestTrait {
21+
TestStruct {}
22+
}
23+
24+
//~ MONO_ITEM fn foo
25+
//~ MONO_ITEM fn <TestStruct as TestTrait>::test_func
26+
27+
trait TestTrait2 {
28+
fn test_func2(&self);
29+
}
30+
31+
struct TestStruct2 {}
32+
33+
impl TestTrait2 for TestStruct2 {
34+
fn test_func2(&self) {
35+
println!("TestStruct2::test_func2");
36+
}
37+
}
38+
39+
#[inline(never)]
40+
pub fn foo2() -> Box<dyn TestTrait2> {
41+
Box::new(TestStruct2 {})
42+
}
43+
44+
//~ MONO_ITEM fn <TestStruct2 as TestTrait2>::test_func2
45+
//~ MONO_ITEM fn alloc::alloc::exchange_malloc
46+
//~ MONO_ITEM fn foo2
47+
//~ MONO_ITEM fn std::alloc::Global::alloc_impl
48+
//~ MONO_ITEM fn std::boxed::Box::<TestStruct2>::new
49+
//~ MONO_ITEM fn std::alloc::Layout::from_size_align_unchecked::precondition_check
50+
//~ MONO_ITEM fn std::ptr::NonNull::<T>::new_unchecked::precondition_check
51+
52+
struct Counter {
53+
count: usize,
54+
}
55+
56+
impl Counter {
57+
fn new() -> Counter {
58+
Counter { count: 0 }
59+
}
60+
}
61+
62+
impl Iterator for Counter {
63+
type Item = usize;
64+
65+
fn next(&mut self) -> Option<Self::Item> {
66+
self.count += 1;
67+
if self.count < 6 { Some(self.count) } else { None }
68+
}
69+
}
70+
71+
#[inline(never)]
72+
pub fn foo3() -> Box<dyn Iterator<Item = usize>> {
73+
Box::new(Counter::new())
74+
}
75+
76+
//~ MONO_ITEM fn <Counter as std::iter::Iterator::advance_by::SpecAdvanceBy>::spec_advance_by
77+
//~ MONO_ITEM fn <Counter as std::iter::Iterator::advance_by::SpecAdvanceBy>::spec_advance_by::{closure#0}
78+
//~ MONO_ITEM fn <Counter as std::iter::Iterator>::advance_by
79+
//~ MONO_ITEM fn <Counter as std::iter::Iterator>::next
80+
//~ MONO_ITEM fn <Counter as std::iter::Iterator>::nth
81+
//~ MONO_ITEM fn <Counter as std::iter::Iterator>::size_hint
82+
//~ MONO_ITEM fn <Counter as std::iter::Iterator>::try_fold::<std::num::NonZero<usize>, {closure@<Counter as std::iter::Iterator::advance_by::SpecAdvanceBy>::spec_advance_by::{closure#0}}, std::option::Option<std::num::NonZero<usize>>>
83+
//~ MONO_ITEM fn <std::option::Option<std::num::NonZero<usize>> as std::ops::FromResidual<std::option::Option<std::convert::Infallible>>>::from_residual
84+
//~ MONO_ITEM fn <std::option::Option<std::num::NonZero<usize>> as std::ops::Try>::branch
85+
//~ MONO_ITEM fn <std::option::Option<std::num::NonZero<usize>> as std::ops::Try>::from_output
86+
//~ MONO_ITEM fn foo3
87+
//~ MONO_ITEM fn std::boxed::Box::<Counter>::new
88+
//~ MONO_ITEM fn Counter::new
89+
//~ MONO_ITEM fn core::fmt::rt::<impl std::fmt::Arguments<'_>>::new_const::<1>

tests/coverage/async.cov-map

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,21 +103,21 @@ Number of file 0 mappings: 3
103103
Highest counter ID seen: (none)
104104

105105
Function name: async::g
106-
Raw bytes (9): 0x[01, 01, 00, 01, 01, 1b, 01, 00, 16]
106+
Raw bytes (9): 0x[01, 01, 00, 01, 01, 1b, 01, 00, 12]
107107
Number of files: 1
108108
- file 0 => $DIR/async.rs
109109
Number of expressions: 0
110110
Number of file 0 mappings: 1
111-
- Code(Counter(0)) at (prev + 27, 1) to (start + 0, 22)
111+
- Code(Counter(0)) at (prev + 27, 1) to (start + 0, 18)
112112
Highest counter ID seen: c0
113113

114114
Function name: async::g::{closure#0} (unused)
115-
Raw bytes (64): 0x[01, 01, 00, 0c, 00, 1b, 17, 00, 18, 00, 01, 0b, 00, 0c, 00, 01, 09, 00, 0a, 00, 00, 0e, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 09, 00, 0a, 00, 00, 0e, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 0e, 00, 10, 00, 02, 01, 00, 02]
115+
Raw bytes (64): 0x[01, 01, 00, 0c, 00, 1b, 13, 00, 14, 00, 01, 0b, 00, 0c, 00, 01, 09, 00, 0a, 00, 00, 0e, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 09, 00, 0a, 00, 00, 0e, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 0e, 00, 10, 00, 02, 01, 00, 02]
116116
Number of files: 1
117117
- file 0 => $DIR/async.rs
118118
Number of expressions: 0
119119
Number of file 0 mappings: 12
120-
- Code(Zero) at (prev + 27, 23) to (start + 0, 24)
120+
- Code(Zero) at (prev + 27, 19) to (start + 0, 20)
121121
- Code(Zero) at (prev + 1, 11) to (start + 0, 12)
122122
- Code(Zero) at (prev + 1, 9) to (start + 0, 10)
123123
- Code(Zero) at (prev + 0, 14) to (start + 0, 23)

tests/coverage/async.coverage

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
LL| |
2525
LL| 0|async fn foo() -> [bool; 10] { [false; 10] } // unused function; executor does not block on `h()`
2626
LL| |
27-
LL| 1|pub async fn g(x: u8) {
28-
^0
27+
LL| 1|async fn g(x: u8) {
28+
^0
2929
LL| 0| match x {
3030
LL| 0| y if e().await == y => (),
3131
LL| 0| y if f().await == y => (),

tests/coverage/async.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ async fn f() -> u8 { 1 }
2424

2525
async fn foo() -> [bool; 10] { [false; 10] } // unused function; executor does not block on `h()`
2626

27-
pub async fn g(x: u8) {
27+
async fn g(x: u8) {
2828
match x {
2929
y if e().await == y => (),
3030
y if f().await == y => (),

tests/ui/async-await/future-sizes/async-awaiting-fut.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
//@ compile-flags: -Z print-type-sizes --crate-type lib
1+
//@ compile-flags: -C panic=abort -Z print-type-sizes --crate-type lib
2+
//@ needs-deterministic-layouts
23
//@ edition:2021
34
//@ build-pass
45
//@ ignore-pass
6+
//@ only-x86_64
57

68
async fn wait() {}
79

tests/ui/async-await/future-sizes/async-awaiting-fut.stdout

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,39 @@ print-type-size variant `Returned`: 1024 bytes
4848
print-type-size upvar `.arg`: 1024 bytes
4949
print-type-size variant `Panicked`: 1024 bytes
5050
print-type-size upvar `.arg`: 1024 bytes
51+
print-type-size type: `std::task::Context<'_>`: 32 bytes, alignment: 8 bytes
52+
print-type-size field `.waker`: 8 bytes
53+
print-type-size field `.local_waker`: 8 bytes
54+
print-type-size field `.ext`: 16 bytes
55+
print-type-size field `._marker`: 0 bytes
56+
print-type-size field `._marker2`: 0 bytes
57+
print-type-size type: `std::panic::Location<'_>`: 24 bytes, alignment: 8 bytes
58+
print-type-size field `.filename`: 16 bytes
59+
print-type-size field `.line`: 4 bytes
60+
print-type-size field `.col`: 4 bytes
61+
print-type-size field `._filename`: 0 bytes
62+
print-type-size type: `core::task::wake::ExtData<'_>`: 16 bytes, alignment: 8 bytes
63+
print-type-size variant `Some`: 16 bytes
64+
print-type-size field `.0`: 16 bytes
65+
print-type-size variant `None`: 0 bytes
66+
print-type-size field `.0`: 0 bytes
67+
print-type-size type: `std::panic::AssertUnwindSafe<core::task::wake::ExtData<'_>>`: 16 bytes, alignment: 8 bytes
68+
print-type-size field `.0`: 16 bytes
69+
print-type-size type: `std::ptr::NonNull<str>`: 16 bytes, alignment: 8 bytes
70+
print-type-size field `.pointer`: 16 bytes
71+
print-type-size type: `std::pin::Pin<&mut {async fn body of big_fut()}>`: 8 bytes, alignment: 8 bytes
72+
print-type-size field `.pointer`: 8 bytes
73+
print-type-size type: `std::pin::Pin<&mut {async fn body of calls_fut<{async fn body of big_fut()}>()}>`: 8 bytes, alignment: 8 bytes
74+
print-type-size field `.pointer`: 8 bytes
75+
print-type-size type: `std::pin::Pin<&mut {async fn body of test()}>`: 8 bytes, alignment: 8 bytes
76+
print-type-size field `.pointer`: 8 bytes
77+
print-type-size type: `std::pin::Pin<&mut {async fn body of wait()}>`: 8 bytes, alignment: 8 bytes
78+
print-type-size field `.pointer`: 8 bytes
79+
print-type-size type: `std::ptr::DynMetadata<dyn std::any::Any>`: 8 bytes, alignment: 8 bytes
80+
print-type-size field `._vtable_ptr`: 8 bytes
81+
print-type-size field `._phantom`: 0 bytes
82+
print-type-size type: `std::ptr::NonNull<std::ptr::metadata::VTable>`: 8 bytes, alignment: 8 bytes
83+
print-type-size field `.pointer`: 8 bytes
5184
print-type-size type: `std::mem::ManuallyDrop<bool>`: 1 bytes, alignment: 1 bytes
5285
print-type-size field `.value`: 1 bytes
5386
print-type-size type: `std::mem::ManuallyDrop<{async fn body of wait()}>`: 1 bytes, alignment: 1 bytes
@@ -70,3 +103,7 @@ print-type-size discriminant: 1 bytes
70103
print-type-size variant `Unresumed`: 0 bytes
71104
print-type-size variant `Returned`: 0 bytes
72105
print-type-size variant `Panicked`: 0 bytes
106+
print-type-size type: `std::marker::PhantomData<&str>`: 0 bytes, alignment: 1 bytes
107+
print-type-size type: `std::marker::PhantomData<*mut ()>`: 0 bytes, alignment: 1 bytes
108+
print-type-size type: `std::marker::PhantomData<dyn std::any::Any>`: 0 bytes, alignment: 1 bytes
109+
print-type-size type: `std::marker::PhantomData<fn(&()) -> &()>`: 0 bytes, alignment: 1 bytes

tests/ui/async-await/future-sizes/large-arg.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
//@ compile-flags: -Z print-type-sizes --crate-type=lib
1+
//@ compile-flags: -C panic=abort -Z print-type-sizes --crate-type=lib
2+
//@ needs-deterministic-layouts
23
//@ edition: 2021
34
//@ build-pass
45
//@ ignore-pass
6+
//@ only-x86_64
57

68
pub async fn test() {
79
let _ = a([0u8; 1024]).await;

0 commit comments

Comments
 (0)