perf(minifier): evaluate ternary branches once in minimize_conditional_expression (#23479)

## What & why

`minimize_conditional_expression` ran two folds — the boolean fold (`a ?
true : false` → `!!a`) and
the number fold (`a ? 1 : 0` → `+a`) — and **each** independently called
`expr.consequent.evaluate_value(ctx)` and
`expr.alternate.evaluate_value(ctx)`. So every ternary
evaluated each branch **twice** (4 `evaluate_value` calls per
`ConditionalExpression`).

`evaluate_value` is a recursive constant-evaluation walk over the
operand subtree. This hoists the two
results and reuses them across both folds (4 → 2 calls per ternary).
`minimize_conditional_expression`
runs in `exit_expression` on every `ConditionalExpression`, across the
fixed-point peephole loop, and
ternaries are common — so this is a hot path.

```rust
let consequent_value = expr.consequent.evaluate_value(ctx);
let alternate_value  = expr.alternate.evaluate_value(ctx);
// boolean fold reads the cached values clone-free (as_ref + variant match == into_boolean)
// number  fold consumes them (into_number) — last use
```

The boolean fold reads the cached values **clone-free** (an `as_ref()` +
`Boolean` variant match,
identical to `into_boolean`); the number fold consumes them. Nothing
mutates the operands between the
two folds (the boolean fold only mutates inside arms that `return`), so
the cached values are valid.

## Allocations (measured)

`cargo allocs` shows a small system-allocation reduction, no
regressions:

| file | sys allocs: before → after |
| --- | --- |
| checker.ts | 166 → 162 (**−4**) |
| pdf.mjs | 2614 → 2613 (**−1**) |

(Arena allocs unchanged.) The primary effect is the halved
`evaluate_value` work (instruction count) —
CodSpeed will arbitrate that on this PR. The change is structured to
never do more work than before.

## Behavior-preserving

`cargo test -p oxc_minifier` (535 tests) passes; no snapshot drift
beyond the allocation snapshot above.

---
Prepared with AI assistance.

---------

Co-authored-by: Cameron Clark <cameron.clark@hey.com>
This commit is contained in:
Yunfei HeandCameron Clark authored and GitHub committed 2026-06-16 09:45:46 +01:00
1 parent e409fe0ee0
commit d1fa6e09af
2 files changed
+19 -12

No files matched your search

@@ -377,16 +377,25 @@ impl<'a> PeepholeOptimizations {
}
}
let consequent_value = expr.consequent.evaluate_value(ctx);
let alternate_value = expr.alternate.evaluate_value(ctx);
// "a ? true : false" => "!!a"
// "a ? false : true" => "!a"
match (
expr.consequent
.evaluate_value(ctx)
.and_then(ConstantValue::into_boolean)
consequent_value
.as_ref()
.and_then(|v| match v {
ConstantValue::Boolean(b) => Some(*b),
_ => None,
})
.filter(|_| !expr.consequent.may_have_side_effects(ctx)),
expr.alternate
.evaluate_value(ctx)
.and_then(ConstantValue::into_boolean)
alternate_value
.as_ref()
.and_then(|v| match v {
ConstantValue::Boolean(b) => Some(*b),
_ => None,
})
.filter(|_| !expr.alternate.may_have_side_effects(ctx)),
) {
(Some(true), Some(false)) => {
@@ -406,12 +415,10 @@ impl<'a> PeepholeOptimizations {
// "a ? 1 : 0" => "+a" (if a is boolean) or "+!!a" (if no parens needed)
// "a ? 0 : 1" => "+!a" (if no parens needed)
match (
expr.consequent
.evaluate_value(ctx)
consequent_value
.and_then(ConstantValue::into_number)
.filter(|_| !expr.consequent.may_have_side_effects(ctx)),
expr.alternate
.evaluate_value(ctx)
alternate_value
.and_then(ConstantValue::into_number)
.filter(|_| !expr.alternate.may_have_side_effects(ctx)),
) {
@@ -1,12 +1,12 @@
File | File size || Sys allocs | Sys reallocs || Arena allocs | Arena reallocs
---------------------------------------------------------------------------------------------------------------------------
checker.ts | 2.92 MB || 166 | 52 || 152742 | 28244
checker.ts | 2.92 MB || 162 | 52 || 152742 | 28244
App.tsx | 415.34 kB || 115 | 49 || 17012 | 2702
RadixUIAdoptionSection.jsx | 2.52 kB || 24 | 20 || 32 | 6
pdf.mjs | 567.30 kB || 2614 | 249 || 47485 | 7740
pdf.mjs | 567.30 kB || 2613 | 249 || 47485 | 7740
antd.js | 6.69 MB || 3084 | 106 || 337233 | 69333