mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
perf(codegen): accept one-shot wrap closures (#23265)
## Summary - Change `Codegen::wrap` from `FnMut` to `FnOnce`, matching how the helper is used: every closure passed to `wrap` is invoked exactly once. - Add an assembly comparison note showing the optimized codegen difference before and after the change. ## `Fn`, `FnMut`, and `FnOnce` ? `Fn`, `FnMut`, and `FnOnce` all describe how a closure may be called: - `Fn` is the most restrictive for the closure body: it is callable through `&self`, can be called repeatedly, and cannot require mutable or consuming access to captured state. - `FnMut` is callable through `&mut self`, can be called repeatedly, and may mutate captured state. - `FnOnce` is callable by value, may consume captured state, and is only guaranteed to be callable once. The trait relationship goes from most specific to most general call capability: ```rust Fn: FnMut FnMut: FnOnce ``` So accepting `FnOnce` is the least restrictive bound for a callback that is only invoked once. It still accepts `Fn` and `FnMut` closures, but it also tells the optimizer that `wrap` does not need a reusable mutable closure object. ## Assembly Impact `Codegen::wrap` is an inline generic helper, so there is no stable standalone `wrap` assembly symbol in release output. The impact shows up in monomorphized call sites such as `Class::gen`, `Function::gen`, and expression `gen_expr` closures. Before, several call sites materialized a closure environment on the stack before calling the closure: ```asm strb w9, [sp, #15] add x9, sp, #15 stp x0, x9, [sp, #16] add x0, sp, #16 bl <...>::{{closure}} ``` After the `FnOnce` bound, the same shape can pass the one-shot closure state directly through registers: ```asm and w1, w2, #0xfffffffd mov x2, x19 bl <...>::{{closure}} ``` Some wrapper frames also shrink. For example, representative `Class::gen` / `Function::gen` paths go from a `64` byte frame to a `48` byte frame: ```asm - sub sp, sp, #64 - stp x20, x19, [sp, #32] - stp x29, x30, [sp, #48] + sub sp, sp, #48 + stp x20, x19, [sp, #16] + stp x29, x30, [sp, #32] ``` Several restored-frame return paths also become tail calls: ```asm ldp x29, x30, [sp, #32] ldp x20, x19, [sp, #16] add sp, sp, #48 b <closure or push_slow target> ``` The assembly diff also contains expected local label renumbering noise, such as switch-table suffixes changing from `.318` to `.330`; those are not behavior changes.
This commit is contained in:
1 parent
06e215e441
commit
e89729b178
1 file changed
+1
-1
@@ -524,7 +524,7 @@ impl<'a> Codegen<'a> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn wrap<F: FnMut(&mut Self)>(&mut self, wrap: bool, mut f: F) {
|
||||
fn wrap<F: FnOnce(&mut Self)>(&mut self, wrap: bool, f: F) {
|
||||
if wrap {
|
||||
self.print_ascii_byte(b'(');
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user