perf(parser): reduce code bloat from verify_modifiers monomorphization (#23576)

## What

`verify_modifiers`'s inner cold `report<C, F>` is generic over the diagnostic-creating closure `F` and the parser `Config` `C`. With 3 concrete configs (`NoTokens`/`Tokens`/`Runtime`) and ~13 distinct call-site closures, it was monomorphized **~39 times**, and every copy dragged in the full `sort_unstable_by_key` + filter/collect machinery (quicksort, smallsort, merge, partition, …).

This PR extracts the collect + sort into a **non-generic** helper `collect_disallowed`, so that heavy code — including the entire sort implementation — is compiled **once** rather than ~39 times.

## Why it's safe

It's the cold, invalid-modifier error path: both functions are `#[cold]` + `#[inline(never)]`, so nothing here is on the hot path and there is no runtime cost. Pure refactor — behavior is unchanged.

## Impact

`cargo llvm-lines --lib -p oxc_parser` (dev profile):

| | Lines | Copies |
|---|--:|--:|
| before | 269,493 | 9,564 |
| after | 197,808 | 7,817 |
| **delta** | **−71,685 (−26.6%)** | **−1,747** |

The sort helpers collapse from 39–78 copies each down to 1–2. This is reachable cold code that ships in the binary (not DCE'd), so it reduces both compile time and code size.

Found via a `cargo llvm-lines` sweep of all published crates.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit is contained in:
Boshen committed 2026-06-18 01:41:41 +00:00
1 parent 1b7b17b76b
commit 4058a6a4ec
1 file changed
+16 -6
+16 -6
View File
@@ -812,6 +812,21 @@ impl<C: Config> ParserImpl<'_, C> {
if modifiers.kinds().has_any_not_in(allowed) {
// Invalid modifiers are rare, so handle this case in `#[cold]` function.
// Also `#[inline(never)]` to help `verify_modifiers` to get inlined.
// Non-generic, so the heavy collect + sort code (including the sort implementation)
// is compiled only once, rather than once per `report` instantiation
// (caller closure `F` × parser `Config` = ~39 copies).
#[cold]
#[inline(never)]
fn collect_disallowed(modifiers: &Modifiers, allowed: ModifierKinds) -> Vec<Modifier> {
// Sort modifiers to produce errors in source code order
let mut disallowed_modifiers = modifiers
.iter()
.filter(|modifier| !allowed.contains(modifier.kind))
.collect::<Vec<_>>();
disallowed_modifiers.sort_unstable_by_key(|modifier| modifier.span_start);
disallowed_modifiers
}
#[cold]
#[inline(never)]
fn report<C: Config, F>(
@@ -823,12 +838,7 @@ impl<C: Config> ParserImpl<'_, C> {
) where
F: Fn(&Modifier, Option<ModifierKinds>) -> OxcDiagnostic,
{
// Sort modifiers to produce errors in source code order
let mut disallowed_modifiers = modifiers
.iter()
.filter(|modifier| !allowed.contains(modifier.kind))
.collect::<Vec<_>>();
disallowed_modifiers.sort_unstable_by_key(|modifier| modifier.span_start);
let disallowed_modifiers = collect_disallowed(modifiers, allowed);
debug_assert!(!disallowed_modifiers.is_empty());