mirror of
https://github.com/aphrody-labs/oxc.git
synced 2026-10-17 12:56:09 +00:00
perf(packages/codegen): reduce megamorphic type reads in binCheckAndPrepare (#27460)
Perf optimization. Continuation of #27458. Ensure that costly megamorphic reads of the `type` of `Expression`s happens the minimum of times. Pass `left` (with any `ParenthesizedExpression` wrappers stripped off) and `left.type` into `binCheckAndPrepare`, so in the paths for `??` and `**`, the unwrapping and megamorphic `.type` accesses don't need to be repeated again. `**` is rare, but `??` is quite heavily used in some codebases. Passing these 2 vars as params doesn't cost anything, as `binCheckAndPrepare` gets inlined anyway.
This commit is contained in:
1 parent
3d2f93f87c
commit
961f318571
2 files changed
+34
-15
No files matched your search
@@ -5,11 +5,10 @@ import { CAT_CLOSE_BRACKET, CAT_OTHER } from "./categories.ts";
|
||||
import { write } from "./write.ts";
|
||||
import { printPrivateInExpression, printExpression } from "./expression.ts";
|
||||
import { BIN_PRECEDENCE, CTX_FORBID_IN, PADDED_BIN_OPERATORS } from "./operators.ts";
|
||||
import { withoutParens } from "./parens.ts";
|
||||
import { PREC_CALL, PREC_EXPONENTIATION, PREC_LOWEST, PREC_PREFIX } from "./precedence.ts";
|
||||
|
||||
import type { State } from "../state.ts";
|
||||
import type { LiteralExtras } from "./types.ts";
|
||||
import type { Literal, LiteralExtras } from "./types.ts";
|
||||
import type {
|
||||
BinaryExpression,
|
||||
BinaryOperator,
|
||||
@@ -77,13 +76,13 @@ export function printBinaryish(
|
||||
|
||||
// At the top of each iteration, `left` is `v.e.left`, and `leftType` is its `type`
|
||||
for (;;) {
|
||||
binCheckAndPrepare(v, state);
|
||||
|
||||
while (leftType === "ParenthesizedExpression") {
|
||||
left = (left as ParenthesizedExpression).expression;
|
||||
leftType = left.type;
|
||||
}
|
||||
|
||||
binCheckAndPrepare(v, state, left, leftType);
|
||||
|
||||
let nextLeft;
|
||||
if (leftType === "BinaryExpression") {
|
||||
nextLeft = (left as BinaryExpression | PrivateInExpression).left;
|
||||
@@ -137,8 +136,19 @@ export function printBinaryish(
|
||||
*
|
||||
* `**` is right associative, so its left operand binds tighter, and the rest are the other way round.
|
||||
* `??` may not sit unparenthesized beside `&&` or `||`, which is why either operand can be forced up to `PREC_PREFIX`.
|
||||
*
|
||||
* @param v - The level being prepared. Its `operator` holds the parent level's operator on entry,
|
||||
* and is replaced with this level's own.
|
||||
* @param state - Printer state
|
||||
* @param left - `v.e.left`, with any parens unwrapped
|
||||
* @param leftType - `left.type`
|
||||
*/
|
||||
function binCheckAndPrepare(v: BinaryVisitor, state: State): void {
|
||||
function binCheckAndPrepare(
|
||||
v: BinaryVisitor,
|
||||
state: State,
|
||||
left: Expression,
|
||||
leftType: string,
|
||||
): void {
|
||||
const { e } = v;
|
||||
const eOperator = e.operator;
|
||||
const ePrecedence = BIN_PRECEDENCE[eOperator];
|
||||
@@ -169,27 +179,33 @@ function binCheckAndPrepare(v: BinaryVisitor, state: State): void {
|
||||
v.rightPrecedence = ePrecedence;
|
||||
}
|
||||
|
||||
// The operands can be any expression, so reading their `type` is a megamorphic load.
|
||||
// The left operand arrives from `printBinaryish` already unwrapped, with its `type` already read.
|
||||
// The right operand's `type` is read only once, while unwrapping parens.
|
||||
if (eOperator === "??") {
|
||||
// Nullish coalescing cannot mix with && / || unparenthesized
|
||||
const left = withoutParens(e.left);
|
||||
if (left.type === "LogicalExpression" && left.operator !== "??") {
|
||||
if (leftType === "LogicalExpression" && (left as LogicalExpression).operator !== "??") {
|
||||
v.leftPrecedence = PREC_PREFIX;
|
||||
}
|
||||
|
||||
const right = withoutParens(e.right);
|
||||
if (right.type === "LogicalExpression" && right.operator !== "??") {
|
||||
let { right } = e;
|
||||
let rightType = right.type;
|
||||
while (rightType === "ParenthesizedExpression") {
|
||||
right = (right as ParenthesizedExpression).expression;
|
||||
rightType = right.type;
|
||||
}
|
||||
if (rightType === "LogicalExpression" && (right as LogicalExpression).operator !== "??") {
|
||||
v.rightPrecedence = PREC_PREFIX;
|
||||
}
|
||||
} else if (eOperator === "**") {
|
||||
// The base of `**` must be an `UpdateExpression`.
|
||||
// Unary/await bases and negative-printing literals must be parenthesized.
|
||||
const left = withoutParens(e.left);
|
||||
if (
|
||||
left.type === "UnaryExpression"
|
||||
|| left.type === "AwaitExpression"
|
||||
|| (TS && left.type === "TSTypeAssertion")
|
||||
|| (left.type === "Literal"
|
||||
&& (typeof left.value === "number" || (left as LiteralExtras).bigint != null))
|
||||
leftType === "UnaryExpression"
|
||||
|| leftType === "AwaitExpression"
|
||||
|| (TS && leftType === "TSTypeAssertion")
|
||||
|| (leftType === "Literal"
|
||||
&& (typeof (left as Literal).value === "number" || (left as LiteralExtras).bigint != null))
|
||||
) {
|
||||
v.leftPrecedence = PREC_CALL;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ export interface UnknownNode {
|
||||
type: "<unknown node>";
|
||||
}
|
||||
|
||||
/** An `Expression` which is a literal. */
|
||||
export type Literal = Extract<ESTree.Expression, { type: "Literal" }>;
|
||||
|
||||
/**
|
||||
* Properties which only some literals carry. `printLiteral` reads them on any literal to tell
|
||||
* regexes and bigints apart from the rest.
|
||||
|
||||
Reference in new issue
Block a user