Skip to content

Commit d33c112

Browse files
Boshenclaude
andcommitted
perf(parser): use range checks for is_any_keyword() and is_number()
Replace multi-function calls and multiple enum variant checks with simple range checks, reducing assembly instructions in hot paths. ## Changes **`is_any_keyword()`**: - Before: Called 4 separate functions (`is_reserved_keyword()`, `is_contextual_keyword()`, `is_strict_mode_contextual_keyword()`, `is_future_reserved_keyword()`) checking 70+ enum variants with early returns - After: Single range check `Await..=Yield` since all keywords are contiguous in the enum **`is_number()`**: - Before: Matched 11 separate enum variants - After: Single range check `Decimal..=HexBigInt` since all numeric literals are contiguous ## Assembly Impact Multi-function approach generated **5 instructions** with complex bitmask setup: ```asm mov x8, #992 movk x8, #992, lsl #16 movk x8, #240, lsl #32 lsr x8, x8, x0 and w0, w8, #0x1 ``` Range check generates **4 instructions** with simple arithmetic: ```asm and w8, w0, #0xff sub w8, w8, #5 cmp w8, #39 cset w0, lo ``` ## Performance - `is_any_keyword()` is called from `advance()` on **every single token** - 20% fewer instructions (5 → 4) - Simpler logic enables better branch prediction - Eliminates complex constant loading Added tests to verify enum layout assumptions remain valid. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 1f5167a commit d33c112

File tree

1 file changed

+35
-18
lines changed

1 file changed

+35
-18
lines changed

crates/oxc_parser/src/lexer/kind.rs

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -211,22 +211,10 @@ impl Kind {
211211
self == Eof
212212
}
213213

214+
/// All numeric literals are contiguous from Decimal..=HexBigInt in the enum.
214215
#[inline]
215216
pub fn is_number(self) -> bool {
216-
matches!(
217-
self,
218-
Float
219-
| Decimal
220-
| Binary
221-
| Octal
222-
| Hex
223-
| PositiveExponential
224-
| NegativeExponential
225-
| DecimalBigInt
226-
| BinaryBigInt
227-
| OctalBigInt
228-
| HexBigInt
229-
)
217+
matches!(self as u8, x if x >= Decimal as u8 && x <= HexBigInt as u8)
230218
}
231219

232220
#[inline] // Inline into `read_non_decimal` - see comment there as to why
@@ -364,12 +352,10 @@ impl Kind {
364352
}
365353

366354
/// [Keywords and Reserved Words](https://tc39.es/ecma262/#sec-keywords-and-reserved-words)
355+
/// All keywords are contiguous from Await..=Yield in the enum, so we use a range check.
367356
#[inline]
368357
pub fn is_any_keyword(self) -> bool {
369-
self.is_reserved_keyword()
370-
|| self.is_contextual_keyword()
371-
|| self.is_strict_mode_contextual_keyword()
372-
|| self.is_future_reserved_keyword()
358+
matches!(self as u8, x if x >= Await as u8 && x <= Yield as u8)
373359
}
374360

375361
#[rustfmt::skip]
@@ -719,3 +705,34 @@ impl Display for Kind {
719705
self.to_str().fmt(f)
720706
}
721707
}
708+
709+
#[cfg(test)]
710+
mod tests {
711+
use super::Kind;
712+
713+
/// Verify that keywords are contiguous in the enum for range check optimization
714+
#[test]
715+
fn test_keyword_range() {
716+
// First keyword
717+
assert!(Kind::Await.is_any_keyword());
718+
// Last keyword
719+
assert!(Kind::Yield.is_any_keyword());
720+
// Not a keyword (before range)
721+
assert!(!Kind::Ident.is_any_keyword());
722+
// Not a keyword (after range)
723+
assert!(!Kind::Amp.is_any_keyword());
724+
}
725+
726+
/// Verify that numeric literals are contiguous in the enum for range check optimization
727+
#[test]
728+
fn test_number_range() {
729+
// First number
730+
assert!(Kind::Decimal.is_number());
731+
// Last number
732+
assert!(Kind::HexBigInt.is_number());
733+
// Not a number (before range)
734+
assert!(!Kind::False.is_number());
735+
// Not a number (after range)
736+
assert!(!Kind::Str.is_number());
737+
}
738+
}

0 commit comments

Comments
 (0)