perf(parser): avoid span lookup for arrow expression body (#23788)

This avoids calling `Expression::span()` when wrapping an expression-bodied arrow function in a synthetic `ExpressionStatement`.

The parser already knows the expression body starts at the current token before parsing and ends at `prev_token_end` afterwards, so this can use `start_span()` / `end_span()` directly. That keeps the returned `Expression` in registers instead of materializing it on the stack just to call the generated `GetSpan` impl.

Before, the optimized parser assembly spilled the returned expression and called `Expression::span()`:

```asm
bl      parse_assignment_expression_or_higher_impl
mov     x20, x0
mov     x22, x1
strh    w21, [x19, #1196]
strb    w0, [sp, #16]
str     x1, [sp, #24]
add     x0, sp, #16
bl      GetSpan_for_Expression_span
...
str     x0, [x21]       ; ExpressionStatement span
strb    w20, [x21, #16] ; Expression tag
str     x22, [x21, #24] ; Expression payload
```

After, the span is built from parser token state and the expression result is written directly into the arena allocation:

```asm
ldr     x20, [x0, #816]  ; current token span before parse
...
bl      parse_assignment_expression_or_higher_impl
strh    w21, [x19, #1196]
ldr     w8, [x19, #1192] ; previous token end after parse
bfi     x20, x8, #32, #32
...
str     x20, [x21]      ; ExpressionStatement span
strb    w0, [x21, #16]  ; Expression tag
str     x1, [x21, #24]  ; Expression payload
```

This also reduced the first monomorphized parse_arrow_function_expression_body stack frame from 432 bytes to 416 bytes in my local release assembly.
This commit is contained in:
camc314 committed 2026-06-25 16:49:01 +00:00
1 parent 025045deae
commit a6d8e45cb9
1 file changed
+3 -2
+3 -2
View File
@@ -1,6 +1,6 @@
use oxc_allocator::{ArenaBox, ArenaVec};
use oxc_ast::{NONE, ast::*};
use oxc_span::{FileExtension, GetSpan};
use oxc_span::FileExtension;
use oxc_syntax::precedence::Precedence;
use super::{FunctionKind, Tristate};
@@ -315,10 +315,11 @@ impl<'a, C: Config> ParserImpl<'a, C> {
let expression = !self.at(Kind::LCurly);
let body = if expression {
// Remove TopLevel context for arrow function expression body
let span = self.start_span();
let expr = self.context_remove(Context::TopLevel, |p| {
p.parse_assignment_expression_or_higher_impl(allow_return_type_in_arrow_function)
});
let span = expr.span();
let span = self.end_span(span);
let expr_stmt = Statement::new_expression_statement(span, expr, self);
FunctionBody::boxed(
span,