kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit a4ff4c6cd46cad0d2e356bd9c5389677c183e84f
parent 8797d6e49ef2c4981803664a667297c39953fcae
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu, 11 Jun 2026 11:45:21 -0700

perf(parse): classify each decl-spec token's keyword identity exactly once

parse_decl_specs re-decided keyword-ness per candidate: ~26 is_kw probes
plus a trailing ident_kw, every token. Add classify_kw() (alias-aware,
mirrors is_kw's match — classify_kw(p,t)==k iff is_kw(p,t,k)) and call it
once at the top of the loop into a local CKw; the dispatch chain and the
typedef-name fall-through compare against it. One classification per
token instead of up to 27.

Byte-identical (only the deliberate HW-SHA link UUID differs from golden;
all -O0/-O1 .o, -g, -S, -E, diagnostics, splice battery unchanged), and
gated on the keyword/alias suites (test-parse-ok 3880/0, test-parse-err
128/0, test-cg-api, test-toy). Measured marginal: type-decl +7.4%,
global-decl +1.7% (best-of-15 vs the same kit without this change).

Diffstat:
Mlang/c/parse/parse_priv.h | 21+++++++++++++++++++++
Mlang/c/parse/parse_type.c | 60+++++++++++++++++++++++++++++++++---------------------------
2 files changed, 54 insertions(+), 27 deletions(-)

diff --git a/lang/c/parse/parse_priv.h b/lang/c/parse/parse_priv.h @@ -465,6 +465,27 @@ static inline int is_kw(const Parser* p, const Tok* t, CKw k) { return 0; } +/* Classify a token's keyword identity ONCE: the canonical keyword (via kw_map) + * or, for the GNU alias spellings, the keyword they alias to. Returns the same + * CKw that `is_kw(p, t, k)` matches on — i.e. `classify_kw(p, t) == k` is exactly + * `is_kw(p, t, k)` for every k — so a caller that tests a token against many + * keywords can classify it once and compare, instead of re-deciding keyword-ness + * per candidate. MUST stay in lockstep with is_kw's alias set above. */ +static inline CKw classify_kw(const Parser* p, const Tok* t) { + Sym s; + CKw k; + if (t->kind != TOK_IDENT) return KW_NONE; + s = t->v.ident; + k = ident_kw_inline(p, s); /* canonical spellings (kw_map) */ + if (k != KW_NONE) return k; + if (s == p->sym_alignof_alias) return KW_ALIGNOF; + if (s == p->sym_asm_alias) return KW_BUILTIN_ASM; + if (s == p->sym_inline_alias || s == p->sym_inline_alias2) return KW_INLINE; + if (s == p->sym_restrict_alias || s == p->sym_restrict_alias2) return KW_RESTRICT; + if (s == p->sym_thread_alias) return KW_THREAD_LOCAL; + return KW_NONE; +} + static inline int c_type_is_scalar(const Type* ty) { return type_is_arith(ty) || type_is_ptr(ty); } diff --git a/lang/c/parse/parse_type.c b/lang/c/parse/parse_type.c @@ -614,6 +614,10 @@ int parse_decl_specs(Parser* p, DeclSpecs* out) { loc = tok_loc(&p->cur); for (;;) { Tok t = p->cur; + /* Classify the token's keyword identity exactly once per iteration; the + * decl-spec dispatch below compares against this instead of re-deciding + * keyword-ness per candidate (was ~26 is_kw probes/token). */ + CKw tkw = classify_kw(p, &t); if (starts_attr(p)) { Attr* a = parse_attribute_spec_list(p); if (a) { @@ -625,8 +629,8 @@ int parse_decl_specs(Parser* p, DeclSpecs* out) { seen = 1; continue; } - if (is_kw(p, &t, KW_STRUCT) || is_kw(p, &t, KW_UNION)) { - TypeKind kind = is_kw(p, &t, KW_STRUCT) ? TY_STRUCT : TY_UNION; + if (tkw == KW_STRUCT || tkw == KW_UNION) { + TypeKind kind = tkw == KW_STRUCT ? TY_STRUCT : TY_UNION; Attr* anon_attrs = NULL; if (tagged_ty || acc.saw_explicit_type) { perr(p, "conflicting type specifiers (struct/union mixed)"); @@ -638,7 +642,7 @@ int parse_decl_specs(Parser* p, DeclSpecs* out) { seen = 1; continue; } - if (is_kw(p, &t, KW_ENUM)) { + if (tkw == KW_ENUM) { Attr* anon_attrs = NULL; if (tagged_ty || acc.saw_explicit_type) { perr(p, "conflicting type specifiers (enum mixed)"); @@ -650,47 +654,47 @@ int parse_decl_specs(Parser* p, DeclSpecs* out) { seen = 1; continue; } - if (is_kw(p, &t, KW_VOID)) { + if (tkw == KW_VOID) { acc.saw_void = 1; acc.saw_explicit_type = 1; advance(p); seen = 1; - } else if (is_kw(p, &t, KW_CHAR)) { + } else if (tkw == KW_CHAR) { acc.saw_char = 1; acc.saw_explicit_type = 1; advance(p); seen = 1; - } else if (is_kw(p, &t, KW_INT)) { + } else if (tkw == KW_INT) { acc.saw_int = 1; acc.saw_explicit_type = 1; advance(p); seen = 1; - } else if (is_kw(p, &t, KW_SHORT)) { + } else if (tkw == KW_SHORT) { acc.saw_short = 1; acc.saw_explicit_type = 1; advance(p); seen = 1; - } else if (is_kw(p, &t, KW_LONG)) { + } else if (tkw == KW_LONG) { acc.long_count++; acc.saw_explicit_type = 1; advance(p); seen = 1; - } else if (is_kw(p, &t, KW_SIGNED)) { + } else if (tkw == KW_SIGNED) { acc.saw_signed = 1; acc.saw_explicit_type = 1; advance(p); seen = 1; - } else if (is_kw(p, &t, KW_UNSIGNED)) { + } else if (tkw == KW_UNSIGNED) { acc.saw_unsigned = 1; acc.saw_explicit_type = 1; advance(p); seen = 1; - } else if (is_kw(p, &t, KW_BOOL)) { + } else if (tkw == KW_BOOL) { acc.saw_bool = 1; acc.saw_explicit_type = 1; advance(p); seen = 1; - } else if (is_kw(p, &t, KW_FLOAT) || is_kw(p, &t, KW_FLOAT16)) { + } else if (tkw == KW_FLOAT || tkw == KW_FLOAT16) { /* _Float16 is intentionally aliased to 32-bit float (a deliberate * approximation; see test/parse/cases/float16_01_decl, which asserts * sizeof(_Float16) == sizeof(float)). Real 16-bit semantics would be a @@ -699,7 +703,7 @@ int parse_decl_specs(Parser* p, DeclSpecs* out) { acc.saw_explicit_type = 1; advance(p); seen = 1; - } else if (is_kw(p, &t, KW_DOUBLE)) { + } else if (tkw == KW_DOUBLE) { acc.saw_double = 1; acc.saw_explicit_type = 1; advance(p); @@ -721,33 +725,33 @@ int parse_decl_specs(Parser* p, DeclSpecs* out) { acc.saw_explicit_type = 1; advance(p); seen = 1; - } else if (is_kw(p, &t, KW_STATIC)) { + } else if (tkw == KW_STATIC) { if (storage_seen) perr(p, "multiple storage-class specifiers"); storage_seen = 1; out->storage_explicit = 1; out->storage = DS_STATIC; advance(p); seen = 1; - } else if (is_kw(p, &t, KW_EXTERN)) { + } else if (tkw == KW_EXTERN) { if (storage_seen) perr(p, "multiple storage-class specifiers"); storage_seen = 1; out->storage_explicit = 1; out->storage = DS_EXTERN; advance(p); seen = 1; - } else if (is_kw(p, &t, KW_CONST)) { + } else if (tkw == KW_CONST) { out->quals |= Q_CONST; advance(p); seen = 1; - } else if (is_kw(p, &t, KW_VOLATILE)) { + } else if (tkw == KW_VOLATILE) { out->quals |= Q_VOLATILE; advance(p); seen = 1; - } else if (is_kw(p, &t, KW_RESTRICT)) { + } else if (tkw == KW_RESTRICT) { out->quals |= Q_RESTRICT; advance(p); seen = 1; - } else if (is_kw(p, &t, KW_ATOMIC)) { + } else if (tkw == KW_ATOMIC) { Tok n = peek1(p); if (is_punct(&n, '(')) { const Type* inner; @@ -767,14 +771,14 @@ int parse_decl_specs(Parser* p, DeclSpecs* out) { out->quals |= Q_ATOMIC; advance(p); seen = 1; - } else if (is_kw(p, &t, KW_TYPEDEF)) { + } else if (tkw == KW_TYPEDEF) { if (storage_seen) perr(p, "multiple storage-class specifiers"); storage_seen = 1; out->storage_explicit = 1; out->storage = DS_TYPEDEF; advance(p); seen = 1; - } else if (is_kw(p, &t, KW_ALIGNAS)) { + } else if (tkw == KW_ALIGNAS) { u32 a = 0; advance(p); /* `_Alignas` */ expect_punct(p, '(', "'(' after _Alignas"); @@ -792,26 +796,26 @@ int parse_decl_specs(Parser* p, DeclSpecs* out) { expect_punct(p, ')', "')' after _Alignas argument"); if (a > out->align) out->align = a; seen = 1; - } else if (is_kw(p, &t, KW_INLINE)) { + } else if (tkw == KW_INLINE) { out->flags |= DF_INLINE; advance(p); seen = 1; - } else if (is_kw(p, &t, KW_THREAD_LOCAL)) { + } else if (tkw == KW_THREAD_LOCAL) { out->flags |= DF_THREAD; advance(p); seen = 1; - } else if (is_kw(p, &t, KW_NORETURN)) { + } else if (tkw == KW_NORETURN) { out->flags |= DF_NORETURN; advance(p); seen = 1; - } else if (is_kw(p, &t, KW_REGISTER)) { + } else if (tkw == KW_REGISTER) { if (storage_seen) perr(p, "multiple storage-class specifiers"); storage_seen = 1; out->storage_explicit = 1; out->storage = DS_REGISTER; advance(p); seen = 1; - } else if (is_kw(p, &t, KW_AUTO)) { + } else if (tkw == KW_AUTO) { if (storage_seen) perr(p, "multiple storage-class specifiers"); storage_seen = 1; out->storage_explicit = 1; @@ -819,7 +823,9 @@ int parse_decl_specs(Parser* p, DeclSpecs* out) { advance(p); seen = 1; } else if (!acc.saw_explicit_type && !tagged_ty && t.kind == TOK_IDENT && - ident_kw(p, t.v.ident) == KW_NONE) { + tkw == KW_NONE) { + /* tkw == KW_NONE here is exactly ident_kw(t)==KW_NONE: this is the + * terminal else-if, so every keyword/alias branch above already failed. */ if (t.v.ident == p->sym_b_va_list) { if (!p->type_b_va_list) p->type_b_va_list = c_abi_va_list_type(p->abi, p->pool);