From 463fc5f2de2f81636c505327357525be9e3c6f0e Mon Sep 17 00:00:00 2001 From: overlookmotel <557937+overlookmotel@users.noreply.github.com> Date: Mon, 9 Dec 2024 15:33:23 +0000 Subject: [PATCH] perf(transformer/logic-assignment-operator): inline `enter_expression` visitor (#7744) Optimization. Inline `enter_expression` visitor, and reduce the size of the function, to keep the path for "nothing to do here, bail out" as fast and compact as possible. From CodSpeed results, it looks like `enter_expression` was already being inlined as perf gain is not the usual +5% we see for inlining `enter_expression` when it wasn't already. But reducing the size of the visitor function still nets a +1% perf gain. --- .../es2021/logical_assignment_operators.rs | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/crates/oxc_transformer/src/es2021/logical_assignment_operators.rs b/crates/oxc_transformer/src/es2021/logical_assignment_operators.rs index 4dc86952ba056..9af520ae75fe1 100644 --- a/crates/oxc_transformer/src/es2021/logical_assignment_operators.rs +++ b/crates/oxc_transformer/src/es2021/logical_assignment_operators.rs @@ -58,7 +58,6 @@ use oxc_allocator::CloneIn; use oxc_ast::ast::*; use oxc_semantic::ReferenceFlags; use oxc_span::SPAN; -use oxc_syntax::operator::{AssignmentOperator, LogicalOperator}; use oxc_traverse::{BoundIdentifier, MaybeBoundIdentifier, Traverse, TraverseCtx}; use crate::TransformCtx; @@ -74,16 +73,28 @@ impl<'a, 'ctx> LogicalAssignmentOperators<'a, 'ctx> { } impl<'a, 'ctx> Traverse<'a> for LogicalAssignmentOperators<'a, 'ctx> { + // `#[inline]` because this is a hot path, and most `Expression`s are not `AssignmentExpression`s + // with a logical operator. So we want to bail out as fast as possible for everything else, + // without the cost of a function call. + #[inline] fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { let Expression::AssignmentExpression(assignment_expr) = expr else { return }; // `&&=` `||=` `??=` - let operator = match assignment_expr.operator { - AssignmentOperator::LogicalAnd => LogicalOperator::And, - AssignmentOperator::LogicalOr => LogicalOperator::Or, - AssignmentOperator::LogicalNullish => LogicalOperator::Coalesce, - _ => return, - }; + let Some(operator) = assignment_expr.operator.to_logical_operator() else { return }; + + self.transform_logical_assignment(expr, operator, ctx); + } +} + +impl<'a, 'ctx> LogicalAssignmentOperators<'a, 'ctx> { + fn transform_logical_assignment( + &mut self, + expr: &mut Expression<'a>, + operator: LogicalOperator, + ctx: &mut TraverseCtx<'a>, + ) { + let Expression::AssignmentExpression(assignment_expr) = expr else { unreachable!() }; // `a &&= c` -> `a && (a = c);` // ^ ^ assign_target @@ -122,9 +133,7 @@ impl<'a, 'ctx> Traverse<'a> for LogicalAssignmentOperators<'a, 'ctx> { *expr = logical_expr; } -} -impl<'a, 'ctx> LogicalAssignmentOperators<'a, 'ctx> { fn convert_identifier( ident: &IdentifierReference<'a>, ctx: &mut TraverseCtx<'a>,