Skip to content

Commit 090c365

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 8708f3c + 4b3e42b commit 090c365

File tree

12 files changed

+141
-15
lines changed

12 files changed

+141
-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: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,36 @@ 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 `.file_bytes_with_nul`: 16 bytes
59+
print-type-size field `.line`: 4 bytes
60+
print-type-size field `.col`: 4 bytes
61+
print-type-size type: `core::task::wake::ExtData<'_>`: 16 bytes, alignment: 8 bytes
62+
print-type-size variant `Some`: 16 bytes
63+
print-type-size field `.0`: 16 bytes
64+
print-type-size variant `None`: 0 bytes
65+
print-type-size field `.0`: 0 bytes
66+
print-type-size type: `std::panic::AssertUnwindSafe<core::task::wake::ExtData<'_>>`: 16 bytes, alignment: 8 bytes
67+
print-type-size field `.0`: 16 bytes
68+
print-type-size type: `std::pin::Pin<&mut {async fn body of big_fut()}>`: 8 bytes, alignment: 8 bytes
69+
print-type-size field `.pointer`: 8 bytes
70+
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
71+
print-type-size field `.pointer`: 8 bytes
72+
print-type-size type: `std::pin::Pin<&mut {async fn body of test()}>`: 8 bytes, alignment: 8 bytes
73+
print-type-size field `.pointer`: 8 bytes
74+
print-type-size type: `std::pin::Pin<&mut {async fn body of wait()}>`: 8 bytes, alignment: 8 bytes
75+
print-type-size field `.pointer`: 8 bytes
76+
print-type-size type: `std::ptr::DynMetadata<dyn std::any::Any>`: 8 bytes, alignment: 8 bytes
77+
print-type-size field `._vtable_ptr`: 8 bytes
78+
print-type-size field `._phantom`: 0 bytes
79+
print-type-size type: `std::ptr::NonNull<std::ptr::metadata::VTable>`: 8 bytes, alignment: 8 bytes
80+
print-type-size field `.pointer`: 8 bytes
5181
print-type-size type: `std::mem::ManuallyDrop<bool>`: 1 bytes, alignment: 1 bytes
5282
print-type-size field `.value`: 1 bytes
5383
print-type-size type: `std::mem::ManuallyDrop<{async fn body of wait()}>`: 1 bytes, alignment: 1 bytes
@@ -70,3 +100,6 @@ print-type-size discriminant: 1 bytes
70100
print-type-size variant `Unresumed`: 0 bytes
71101
print-type-size variant `Returned`: 0 bytes
72102
print-type-size variant `Panicked`: 0 bytes
103+
print-type-size type: `std::marker::PhantomData<*mut ()>`: 0 bytes, alignment: 1 bytes
104+
print-type-size type: `std::marker::PhantomData<dyn std::any::Any>`: 0 bytes, alignment: 1 bytes
105+
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: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,41 @@ 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 `.file_bytes_with_nul`: 16 bytes
69+
print-type-size field `.line`: 4 bytes
70+
print-type-size field `.col`: 4 bytes
71+
print-type-size type: `core::task::wake::ExtData<'_>`: 16 bytes, alignment: 8 bytes
72+
print-type-size variant `Some`: 16 bytes
73+
print-type-size field `.0`: 16 bytes
74+
print-type-size variant `None`: 0 bytes
75+
print-type-size field `.0`: 0 bytes
76+
print-type-size type: `std::panic::AssertUnwindSafe<core::task::wake::ExtData<'_>>`: 16 bytes, alignment: 8 bytes
77+
print-type-size field `.0`: 16 bytes
78+
print-type-size type: `std::pin::Pin<&mut {async fn body of a<[u8; 1024]>()}>`: 8 bytes, alignment: 8 bytes
79+
print-type-size field `.pointer`: 8 bytes
80+
print-type-size type: `std::pin::Pin<&mut {async fn body of b<[u8; 1024]>()}>`: 8 bytes, alignment: 8 bytes
81+
print-type-size field `.pointer`: 8 bytes
82+
print-type-size type: `std::pin::Pin<&mut {async fn body of c<[u8; 1024]>()}>`: 8 bytes, alignment: 8 bytes
83+
print-type-size field `.pointer`: 8 bytes
84+
print-type-size type: `std::pin::Pin<&mut {async fn body of test()}>`: 8 bytes, alignment: 8 bytes
85+
print-type-size field `.pointer`: 8 bytes
86+
print-type-size type: `std::ptr::DynMetadata<dyn std::any::Any>`: 8 bytes, alignment: 8 bytes
87+
print-type-size field `._vtable_ptr`: 8 bytes
88+
print-type-size field `._phantom`: 0 bytes
89+
print-type-size type: `std::ptr::NonNull<std::ptr::metadata::VTable>`: 8 bytes, alignment: 8 bytes
90+
print-type-size field `.pointer`: 8 bytes
91+
print-type-size type: `std::task::Poll<()>`: 1 bytes, alignment: 1 bytes
92+
print-type-size discriminant: 1 bytes
93+
print-type-size variant `Ready`: 0 bytes
94+
print-type-size field `.0`: 0 bytes
95+
print-type-size variant `Pending`: 0 bytes
96+
print-type-size type: `std::marker::PhantomData<*mut ()>`: 0 bytes, alignment: 1 bytes
97+
print-type-size type: `std::marker::PhantomData<dyn std::any::Any>`: 0 bytes, alignment: 1 bytes
98+
print-type-size type: `std::marker::PhantomData<fn(&()) -> &()>`: 0 bytes, alignment: 1 bytes

0 commit comments

Comments
 (0)