From 553dd1d1a0e872e6b6dbf7cacc60224a6eee424b Mon Sep 17 00:00:00 2001 From: Boshen Date: Thu, 29 Oct 2026 13:59:00 +0000 Subject: [PATCH] perf(parser): inline hot paths and fold function dispatch (#28404) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inline twelve parser helpers and fold function-expression recognition into primary dispatch. Keep the lexer identifier scanner shared. Native M3 Max throughput, same `426c1510` base and release settings: | Comparison | Standard, 5 files | Held-out, 3 files | | --- | ---: | ---: | | Main | +4.32% | +3.80% | | Previous revision `651ea83` | +1.12% | +1.06% | Eight standard rounds and six held-out rounds. Background load was monitored: 22/40 standard blocks were uncontended and stable; that subset agreed. Normal ASLR reproduced the +1.12% gain. Seven parser hints were statistically tied. Release parser example: **1,643,216 → 1,659,728 bytes (+1.00%)**, **16,528 bytes smaller** than the previous revision. `cargo asm`: shared scanning shrinks `L_A` from 936 to 308 instruction bytes while restoring 23 static scanner calls; full parser inlining removes two static identifier-reference calls. The dispatch-only comparison shrinks the primary/LHS helper from 1,012 to 952 instruction bytes. Final paired `xcrun xctrace` CPU-counter recordings had no detected contention; they do not establish a cache-miss reduction. AI-assisted with Codex. --- crates/oxc_parser/src/js/expression.rs | 43 +++++++++++++++++++++----- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/crates/oxc_parser/src/js/expression.rs b/crates/oxc_parser/src/js/expression.rs index 1a8a086fa7..756dc6d847 100644 --- a/crates/oxc_parser/src/js/expression.rs +++ b/crates/oxc_parser/src/js/expression.rs @@ -93,6 +93,8 @@ impl<'a, C: Config> ParserImpl<'a, C> { Expression::Identifier(self.alloc(IdentifierReference::new(span, name, self))) } + #[expect(clippy::inline_always)] + #[inline(always)] pub(crate) fn parse_identifier_reference(&mut self) -> IdentifierReference<'a> { // allow `await` and `yield`, let semantic analysis report error let kind = self.cur_kind(); @@ -156,6 +158,8 @@ impl<'a, C: Config> ParserImpl<'a, C> { LabelIdentifier::new(span, name, self) } + #[expect(clippy::inline_always)] + #[inline(always)] pub(crate) fn parse_identifier_name(&mut self) -> IdentifierName<'a> { if !self.cur_kind().is_identifier_name() { return self.unexpected(); @@ -266,23 +270,28 @@ impl<'a, C: Config> ParserImpl<'a, C> { /// `RegularExpressionLiteral` /// `TemplateLiteral`[?Yield, ?Await, ~Tagged] /// `CoverParenthesizedExpressionAndArrowParameterList`[?Yield, ?Await] + #[expect(clippy::inline_always)] + #[inline(always)] fn parse_primary_expression(&mut self) -> Expression<'a> { // Ordinary identifiers do not need async-function or contextual-keyword disambiguation. if self.at(Kind::Ident) { return self.parse_identifier_expression(); } - // FunctionExpression, GeneratorExpression - // AsyncFunctionExpression, AsyncGeneratorExpression - if self.at_function_with_async() { - let start = self.cur_start(); - let r#async = self.eat(Kind::Async); - return self.parse_function_expression(start, r#async); - } - let kind = self.cur_kind(); match kind { // Fast path: `Ident` is already classified as a valid identifier reference. Kind::Ident => self.parse_plain_identifier_expression(), + Kind::Function => self.parse_function_expression(self.cur_start(), false), + Kind::Async + if { + let token = self.lexer.peek_token(); + token.kind() == Kind::Function && !token.is_on_new_line() + } => + { + let start = self.cur_start(); + self.bump_any(); + self.parse_function_expression(start, true) + } // ArrayLiteral Kind::LBrack => self.parse_array_expression(), // ObjectLiteral @@ -802,6 +811,8 @@ impl<'a, C: Config> ParserImpl<'a, C> { self.with_pure_comments(Self::parse_lhs_expression_or_higher_impl) } + #[expect(clippy::inline_always)] + #[inline(always)] fn parse_lhs_expression_or_higher_impl(&mut self) -> Expression<'a> { let start = self.cur_start(); // `MemberExpression` @@ -842,6 +853,8 @@ impl<'a, C: Config> ParserImpl<'a, C> { } } + #[expect(clippy::inline_always)] + #[inline(always)] fn with_pure_comments( &mut self, parse: impl FnOnce(&mut Self) -> Expression<'a>, @@ -912,6 +925,8 @@ impl<'a, C: Config> ParserImpl<'a, C> { } /// parse rhs of a member expression, starting from lhs + #[expect(clippy::inline_always)] + #[inline(always)] fn parse_member_expression_rest( &mut self, lhs_start: u32, @@ -1015,6 +1030,8 @@ impl<'a, C: Config> ParserImpl<'a, C> { /// Section 13.3 `MemberExpression` /// static member `a.b` + #[expect(clippy::inline_always)] + #[inline(always)] fn parse_static_member_expression( &mut self, lhs_start: u32, @@ -1219,6 +1236,8 @@ impl<'a, C: Config> ParserImpl<'a, C> { lhs } + #[expect(clippy::inline_always)] + #[inline(always)] fn parse_call_arguments( &mut self, lhs_start: u32, @@ -1258,6 +1277,8 @@ impl<'a, C: Config> ParserImpl<'a, C> { } /// Section 13.4 Update Expression + #[expect(clippy::inline_always)] + #[inline(always)] fn parse_update_expression(&mut self, lhs_start: u32) -> Expression<'a> { let kind = self.cur_kind(); // ++ -- prefix update expressions @@ -1308,6 +1329,8 @@ impl<'a, C: Config> ParserImpl<'a, C> { } /// Section 13.5 Unary Expression + #[expect(clippy::inline_always)] + #[inline(always)] pub(crate) fn parse_unary_expression_or_higher(&mut self, lhs_start: u32) -> Expression<'a> { match self.cur_kind() { // `UnaryExpression : (delete | void | typeof | + | - | ~ | !) UnaryExpression` @@ -1365,6 +1388,8 @@ impl<'a, C: Config> ParserImpl<'a, C> { Expression::new_unary_expression(self.end_span(start), operator, argument, self) } + #[expect(clippy::inline_always)] + #[inline(always)] pub(crate) fn parse_binary_expression_or_higher( &mut self, lhs_precedence: Precedence, @@ -1560,6 +1585,8 @@ impl<'a, C: Config> ParserImpl<'a, C> { /// `ConditionalExpression`[In, Yield, Await] : /// `ShortCircuitExpression`[?In, ?Yield, ?Await] /// `ShortCircuitExpression`[?In, ?Yield, ?Await] ? `AssignmentExpression`[+In, ?Yield, ?Await] : `AssignmentExpression`[?In, ?Yield, ?Await] + #[expect(clippy::inline_always)] + #[inline(always)] fn parse_conditional_expression_rest( &mut self, lhs_start: u32,