Skip to content

Commit da114a3

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 283a074 + eaf2feb commit da114a3

File tree

12 files changed

+153
-15
lines changed

12 files changed

+153
-15
lines changed

compiler/rustc_monomorphize/src/collector.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1504,7 +1504,20 @@ impl<'v> RootCollector<'_, 'v> {
15041504
fn process_nested_body(&mut self, def_id: LocalDefId) {
15051505
match self.tcx.def_kind(def_id) {
15061506
DefKind::Closure => {
1507-
if self.strategy == MonoItemCollectionStrategy::Eager
1507+
// for 'pub async fn foo(..)' also trying to monomorphize foo::{closure}
1508+
let is_pub_fn_coroutine =
1509+
match *self.tcx.type_of(def_id).instantiate_identity().kind() {
1510+
ty::Coroutine(cor_id, _args) => {
1511+
let tcx = self.tcx;
1512+
let parent_id = tcx.parent(cor_id);
1513+
tcx.def_kind(parent_id) == DefKind::Fn
1514+
&& tcx.asyncness(parent_id).is_async()
1515+
&& tcx.visibility(parent_id).is_public()
1516+
}
1517+
ty::Closure(..) | ty::CoroutineClosure(..) => false,
1518+
_ => unreachable!(),
1519+
};
1520+
if (self.strategy == MonoItemCollectionStrategy::Eager || is_pub_fn_coroutine)
15081521
&& !self
15091522
.tcx
15101523
.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+
}

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: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
//@ compile-flags: -Z print-type-sizes --crate-type lib
1+
//@ compile-flags: -C panic=abort -Z print-type-sizes --crate-type lib
22
//@ edition:2021
33
//@ build-pass
44
//@ ignore-pass
5+
//@ only-x86_64
56

67
async fn wait() {}
78

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: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
//@ compile-flags: -Z print-type-sizes --crate-type=lib
1+
//@ compile-flags: -C panic=abort -Z print-type-sizes --crate-type=lib
22
//@ edition: 2021
33
//@ build-pass
44
//@ ignore-pass
5+
//@ only-x86_64
56

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

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,45 @@ print-type-size variant `Returned`: 1024 bytes
5858
print-type-size upvar `.t`: 1024 bytes
5959
print-type-size variant `Panicked`: 1024 bytes
6060
print-type-size upvar `.t`: 1024 bytes
61+
print-type-size type: `std::task::Context<'_>`: 32 bytes, alignment: 8 bytes
62+
print-type-size field `.waker`: 8 bytes
63+
print-type-size field `.local_waker`: 8 bytes
64+
print-type-size field `.ext`: 16 bytes
65+
print-type-size field `._marker`: 0 bytes
66+
print-type-size field `._marker2`: 0 bytes
67+
print-type-size type: `std::panic::Location<'_>`: 24 bytes, alignment: 8 bytes
68+
print-type-size field `.filename`: 16 bytes
69+
print-type-size field `.line`: 4 bytes
70+
print-type-size field `.col`: 4 bytes
71+
print-type-size field `._filename`: 0 bytes
72+
print-type-size type: `core::task::wake::ExtData<'_>`: 16 bytes, alignment: 8 bytes
73+
print-type-size variant `Some`: 16 bytes
74+
print-type-size field `.0`: 16 bytes
75+
print-type-size variant `None`: 0 bytes
76+
print-type-size field `.0`: 0 bytes
77+
print-type-size type: `std::panic::AssertUnwindSafe<core::task::wake::ExtData<'_>>`: 16 bytes, alignment: 8 bytes
78+
print-type-size field `.0`: 16 bytes
79+
print-type-size type: `std::ptr::NonNull<str>`: 16 bytes, alignment: 8 bytes
80+
print-type-size field `.pointer`: 16 bytes
81+
print-type-size type: `std::pin::Pin<&mut {async fn body of a<[u8; 1024]>()}>`: 8 bytes, alignment: 8 bytes
82+
print-type-size field `.pointer`: 8 bytes
83+
print-type-size type: `std::pin::Pin<&mut {async fn body of b<[u8; 1024]>()}>`: 8 bytes, alignment: 8 bytes
84+
print-type-size field `.pointer`: 8 bytes
85+
print-type-size type: `std::pin::Pin<&mut {async fn body of c<[u8; 1024]>()}>`: 8 bytes, alignment: 8 bytes
86+
print-type-size field `.pointer`: 8 bytes
87+
print-type-size type: `std::pin::Pin<&mut {async fn body of test()}>`: 8 bytes, alignment: 8 bytes
88+
print-type-size field `.pointer`: 8 bytes
89+
print-type-size type: `std::ptr::DynMetadata<dyn std::any::Any>`: 8 bytes, alignment: 8 bytes
90+
print-type-size field `._vtable_ptr`: 8 bytes
91+
print-type-size field `._phantom`: 0 bytes
92+
print-type-size type: `std::ptr::NonNull<std::ptr::metadata::VTable>`: 8 bytes, alignment: 8 bytes
93+
print-type-size field `.pointer`: 8 bytes
94+
print-type-size type: `std::task::Poll<()>`: 1 bytes, alignment: 1 bytes
95+
print-type-size discriminant: 1 bytes
96+
print-type-size variant `Ready`: 0 bytes
97+
print-type-size field `.0`: 0 bytes
98+
print-type-size variant `Pending`: 0 bytes
99+
print-type-size type: `std::marker::PhantomData<&str>`: 0 bytes, alignment: 1 bytes
100+
print-type-size type: `std::marker::PhantomData<*mut ()>`: 0 bytes, alignment: 1 bytes
101+
print-type-size type: `std::marker::PhantomData<dyn std::any::Any>`: 0 bytes, alignment: 1 bytes
102+
print-type-size type: `std::marker::PhantomData<fn(&()) -> &()>`: 0 bytes, alignment: 1 bytes

0 commit comments

Comments
 (0)