@@ -59,11 +59,27 @@ pub fn maybe_grow<R, F: FnOnce() -> R>(red_zone: usize, stack_size: usize, callb
59
59
/// The closure will still be on the same thread as the caller of `grow`.
60
60
/// This will allocate a new stack with at least `stack_size` bytes.
61
61
pub fn grow < R , F : FnOnce ( ) -> R > ( stack_size : usize , callback : F ) -> R {
62
+ // Measuring with cargo-llvm-lines revealed that `psm::on_stack::with_on_stack`
63
+ // was monomorphized 1552 times in rustc and was responsible for 1.5% of
64
+ // rustc's total llvm IR lines. That takes time for LLVM to process.
65
+ // Converting the generic callback to a dynamic one removes all that duplication.
66
+ let mut opt_callback = Some ( callback) ;
62
67
let mut ret = None ;
63
68
let ret_ref = & mut ret;
64
- _grow ( stack_size, move || {
65
- * ret_ref = Some ( callback ( ) ) ;
66
- } ) ;
69
+
70
+ // This wrapper around `callback` achieves two things:
71
+ // * It converts the `impl FnOnce` to a `dyn FnMut`.
72
+ // `dyn` because we want it to not be generic, and
73
+ // `FnMut` because we can't pass a `dyn FnOnce` around without boxing it.
74
+ // * It eliminates the generic return value, by writing it to the stack of this function.
75
+ // Otherwise the closure would have to return an unsized value, which isn't possible.
76
+ let dyn_callback: & mut dyn FnMut ( ) = & mut || {
77
+ let taken_callback = opt_callback. take ( ) . unwrap ( ) ;
78
+ * ret_ref = Some ( taken_callback ( ) ) ;
79
+ } ;
80
+
81
+ // _grow is only monomorphized once with a `dyn` argument.
82
+ _grow ( stack_size, dyn_callback) ;
67
83
ret. unwrap ( )
68
84
}
69
85
0 commit comments