kit

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

pp.c (52007B)


      1 /* C11 preprocessor (translation phase 4).
      2  *
      3  * Streams tokens via pp_next: directives are consumed, macro invocations are
      4  * expanded, and TOK_NEWLINE is preserved so pp_emit_text can reconstruct the
      5  * line structure of the source.
      6  *
      7  * The token-source stack carries either a Lexer (file or #include'd file) or
      8  * a pre-built Tok[] buffer (macro expansion). A macro replacement-list buffer
      9  * is a "disabled frame" (cpplib model): while it is live on the source stack
     10  * its owning macro's disabled_depth is non-zero, so a recursive occurrence of
     11  * that macro during rescan is returned permanently un-expanded (TF_NO_EXPAND)
     12  * rather than re-replaced. This replaces the per-token Prosser hideset.
     13  *
     14  * Residual module: source stack, pp_next / pp_next_raw (public streaming),
     15  * pp_new/free, predefined macros, lifecycle, keyword interning. */
     16 
     17 #include <kit/compile.h>
     18 #include <kit/source.h>
     19 
     20 #include "pp/pp_priv.h"
     21 
     22 /* ============================================================
     23  * Source stack
     24  * ============================================================ */
     25 
     26 static TokSrc* src_top(Pp* pp) {
     27   return pp->nsources ? &pp->sources[pp->nsources - 1] : NULL;
     28 }
     29 
     30 void src_push(Pp* pp, TokSrc s) {
     31   if (pp->nsources == pp->sources_cap) {
     32     u32 nc = pp->sources_cap ? pp->sources_cap * 2 : 8;
     33     pp->sources =
     34         (TokSrc*)pp_xrealloc(pp, pp->sources, sizeof(TokSrc) * pp->sources_cap,
     35                              sizeof(TokSrc) * nc, _Alignof(TokSrc));
     36     pp->sources_cap = nc;
     37   }
     38   pp->sources[pp->nsources++] = s;
     39 }
     40 
     41 void src_pop(Pp* pp) {
     42   TokSrc* t;
     43   if (!pp->nsources) return;
     44   t = &pp->sources[pp->nsources - 1];
     45   /* Re-enable the macro whose replacement-list frame this is (cpplib
     46    * disabled-frame model): popping the frame ends the rescan during which the
     47    * macro was unavailable. Balances the increment in push_buf_uniform/replay. */
     48   if (t->disabled_owner) --t->disabled_owner->disabled_depth;
     49   if (t->kind == SRC_LEX && t->lex) {
     50     /* Commit the multiple-include memo for this file before closing it: a
     51      * fully #ifndef-wrapped file records its controlling macro; a #pragma once
     52      * file records the once flag. A later #include of the same resolved path
     53      * then skips re-lexing it entirely (see do_include). */
     54     if (t->path_key && pp->inc_cache_ready) {
     55       IncEntry* e = IncCache_get(&pp->inc_cache, t->path_key);
     56       if (e) {
     57         if (t->once) e->once = 1;
     58         if (t->guard_state == GUARD_AFTER && t->guard_macro)
     59           e->guard = t->guard_macro;
     60       }
     61     }
     62     lex_close(t->lex);
     63     t->lex = NULL;
     64   }
     65   --pp->nsources;
     66 }
     67 
     68 /* Synthesize a lean EOF token into *out. */
     69 static void tok_eof(Tok* out) {
     70   out->kind = TOK_EOF;
     71   out->flags = 0;
     72   out->aux = 0;
     73   out->loc = (LocRef){0, 0};
     74   out->text = (TextRef){TEXT_NONE, 0, 0, 0};
     75 }
     76 
     77 /* Read next raw token from the top source, writing it through `out`. Sets
     78  * *out to TOK_EOF when the stack is empty. Pops empty buffer/lexer sources
     79  * as it descends. `src_kind_out`, if non-NULL, receives the kind of the
     80  * source the token came from (SRC_LEX vs SRC_BUF). Used by pp_next_raw to
     81  * gate directive recognition to lex-sourced tokens only — a `#` produced by
     82  * macro expansion never starts a directive (§6.10.3.4 ¶3). */
     83 void src_next_raw_into(Pp* pp, Tok* out, u8* src_kind_out) {
     84   TokSrc* s;
     85   while ((s = src_top(pp)) != NULL) {
     86     if (s->kind == SRC_BUF) {
     87       if (s->i < s->n) {
     88         *out = s->toks[s->i];
     89         if (src_kind_out) *src_kind_out = SRC_BUF;
     90         /* Pointer-replayed body: apply the per-invocation loc and (first token
     91          * only) BOL/SPACE flags the old fresh-copy path baked in. */
     92         if (s->has_loc_override) {
     93           if (s->i == 0) {
     94             out->flags = (u16)((out->flags & ~(TF_AT_BOL | TF_HAS_SPACE)) |
     95                                s->first_flags_or);
     96           }
     97           out->loc = s->loc_override;
     98         }
     99         ++s->i;
    100         return;
    101       }
    102       if (s->scope_top) {
    103         tok_eof(out);
    104         if (src_kind_out) *src_kind_out = SRC_BUF;
    105         return;
    106       }
    107       src_pop(pp);
    108       continue;
    109     }
    110     /* SRC_LEX */
    111     lex_next(s->lex, out);
    112     if (out->kind == TOK_EOF) {
    113       if (pp->nsources > 1) {
    114         src_pop(pp);
    115         continue;
    116       }
    117       if (src_kind_out) *src_kind_out = SRC_LEX;
    118       return;
    119     }
    120     /* Multiple-include-guard detection: real code before the opening guard
    121      * directive, or after the controlling #endif, disqualifies the whole-file
    122      * #ifndef guard. A newline is whitespace and a beginning-of-line `#` is a
    123      * directive introducer — both transparent; anything else is content. */
    124     if (!pp->reading_directive &&
    125         (s->guard_state == GUARD_START || s->guard_state == GUARD_AFTER) &&
    126         out->kind != TOK_NEWLINE &&
    127         !(out->kind == TOK_PP_HASH && (out->flags & TF_AT_BOL))) {
    128       s->guard_state = GUARD_FAILED;
    129     }
    130     /* #line numbering is no longer applied here: a lean token carries only a
    131      * byte offset, and the #line delta is recorded as a positional overlay
    132      * segment (see pp_add_line_seg) applied lazily by pp_materialize_loc. */
    133     if (src_kind_out) *src_kind_out = SRC_LEX;
    134     return;
    135   }
    136   tok_eof(out);
    137   if (src_kind_out) *src_kind_out = SRC_LEX;
    138 }
    139 
    140 /* Thin by-value shim for the cold/general callers (arg collection, paren
    141  * peek) that pass a NULL src_kind and don't sit in the hot -E loop. */
    142 Tok src_next_raw(Pp* pp, u8* src_kind_out) {
    143   Tok t;
    144   src_next_raw_into(pp, &t, src_kind_out);
    145   return t;
    146 }
    147 
    148 /* ============================================================
    149  * Buffer source push helpers
    150  * ============================================================ */
    151 
    152 /* Push a non-macro buffer source (pushed-back peek tokens, a #pragma / #embed
    153  * payload, an arg-prescan scope). No disabled frame: these payloads do not
    154  * disable any macro during their rescan. */
    155 void push_buf(Pp* pp, Tok* toks, u32 n) {
    156   TokSrc s;
    157   memset(&s, 0, sizeof(s));
    158   s.kind = SRC_BUF;
    159   s.toks = toks;
    160   s.i = 0;
    161   s.n = n;
    162   src_push(pp, s);
    163 }
    164 
    165 /* Push a macro replacement-list buffer (function-like substituted body, paste
    166  * result). `owner` is the macro being expanded: marking the frame with it and
    167  * incrementing owner->disabled_depth makes the macro unavailable for the whole
    168  * rescan of this replacement (cpplib disabled-frame model); src_pop decrements
    169  * it. Set the owner on `s` BEFORE src_push (which copies by value); the
    170  * increment lands on the macro itself, which is fine. */
    171 void push_buf_uniform(Pp* pp, Tok* toks, u32 n, Macro* owner) {
    172   TokSrc s;
    173   memset(&s, 0, sizeof(s));
    174   s.kind = SRC_BUF;
    175   s.toks = toks;
    176   s.i = 0;
    177   s.n = n;
    178   s.disabled_owner = owner;
    179   if (owner) ++owner->disabled_depth;
    180   src_push(pp, s);
    181 }
    182 
    183 /* Push an immutable token buffer (a no-`##` macro body) for pointer-replay.
    184  * The const-cast on `toks` is sound: replay is strictly read-only — the two
    185  * read sites (src_next_raw_into, the pp_pull_into fast path) load each token by
    186  * value and never write back through s->toks. The per-read loc/flags override
    187  * (has_loc_override) reproduces the mutations the old fresh-copy path baked in,
    188  * so the shared body is surfaced byte-for-byte without copying it. */
    189 void push_buf_replay(Pp* pp, const Tok* toks, u32 n, Macro* owner,
    190                      LocRef loc_override, u16 first_flags_or) {
    191   TokSrc s;
    192   memset(&s, 0, sizeof(s));
    193   s.kind = SRC_BUF;
    194   s.toks = (Tok*)toks;
    195   s.has_loc_override = 1;
    196   s.loc_override = loc_override;
    197   s.first_flags_or = first_flags_or;
    198   s.i = 0;
    199   s.n = n;
    200   s.disabled_owner = owner;
    201   if (owner) ++owner->disabled_depth;
    202   src_push(pp, s);
    203 }
    204 
    205 /* ============================================================
    206  * Public streaming entries
    207  * ============================================================ */
    208 
    209 void pp_next_parse(Pp* pp, Tok* out) {
    210   /* Public parser-feed stream: filter newlines so consumers like the C parser
    211    * don't need to handle them. pp_emit_text uses pp_next_raw via its own loop.
    212    *
    213    * The top-of-loop read uses pp_next_into, which already drops non-directive
    214    * newlines internally; the peek-after-hash (t2) and pragma-swallow reads
    215    * below stay on pp_next_raw because they must still see a TOK_NEWLINE (as a
    216    * non-pragma push-back token, and as the swallow terminator).
    217    *
    218    * Also drop forwarded `#pragma` lines: do_pragma pushes the directive back
    219    * onto the source stack so pp_emit_text can re-emit it verbatim in cpp mode,
    220    * but the C parser (cc mode) would see the trailing tokens as stray
    221    * identifiers. When we see TOK_PP_HASH followed by `pragma`, swallow tokens
    222    * through the next NEWLINE. */
    223   for (;;) {
    224     Tok t;
    225     pp_next_into(pp, &t);
    226     if (t.kind == TOK_PP_HASH) {
    227       Tok t2;
    228       pp_next_raw(pp, &t2);
    229       if (t2.kind == TOK_IDENT && tok_ident(&t2) == pp->sym_pragma) {
    230         for (;;) {
    231           Tok tt;
    232           pp_next_raw(pp, &tt);
    233           if (tt.kind == TOK_NEWLINE || tt.kind == TOK_EOF) break;
    234         }
    235         continue;
    236       }
    237       /* Not a pragma — push the peeked token back as a 1-element buffer
    238        * so the next pp_next_raw returns it, and surface the hash now. */
    239       Tok* keep = arena_array(pp->xarena, Tok, 1);
    240       keep[0] = t2;
    241       push_buf(pp, keep, 1);
    242       *out = t;
    243       return;
    244     }
    245     *out = t;
    246     return;
    247   }
    248 }
    249 
    250 /* ============================================================
    251  * pp_emit_text
    252  * ============================================================ */
    253 
    254 /* Stage output bytes into a caller buffer, flushing to the Writer only when it
    255  * fills (or for a pathologically long spelling). Collapses the former ~2
    256  * indirect kit_writer_write calls per token — each a function-pointer call into
    257  * a tiny memcpy — into one bulk write per buffer-full. (fdw_write keeps its own
    258  * 64 KB buffer; this second cheap staging layer removes the per-token call +
    259  * bounds-check, not redundant copying of the bulk.) */
    260 static void pp_emit_stage(Writer* out, char* buf, size_t cap, size_t* on,
    261                           const char* s, size_t n) {
    262   if (!n) return;
    263   if (n > cap) {
    264     if (*on) {
    265       (void)kit_writer_write(out, buf, *on);
    266       *on = 0;
    267     }
    268     (void)kit_writer_write(out, s, n);
    269     return;
    270   }
    271   if (*on + n > cap) {
    272     (void)kit_writer_write(out, buf, *on);
    273     *on = 0;
    274   }
    275   memcpy(buf + *on, s, n);
    276   *on += n;
    277 }
    278 
    279 void pp_emit_text(Pp* pp, Writer* out) {
    280   char obuf[16384];
    281   size_t on = 0;
    282   int at_bol = 1;
    283   for (;;) {
    284     Tok t;
    285     KitSlice s;
    286     pp_next_raw(pp, &t);
    287     if (t.kind == TOK_EOF) break;
    288     if (t.kind == TOK_NEWLINE) {
    289       pp_emit_stage(out, obuf, sizeof obuf, &on, "\n", 1);
    290       at_bol = 1;
    291       continue;
    292     }
    293     if (!at_bol && (t.flags & (TF_HAS_SPACE | TF_AT_BOL))) {
    294       /* TF_AT_BOL on a non-leading output token means the source
    295        * had a line break here that the line-tracking cursor isn't
    296        * preserving — fall back to a single space so the tokens
    297        * don't run together. */
    298       pp_emit_stage(out, obuf, sizeof obuf, &on, " ", 1);
    299     }
    300     s = pp_text_slice(pp, &t);
    301     if (s.len) pp_emit_stage(out, obuf, sizeof obuf, &on, s.s, s.len);
    302     at_bol = 0;
    303   }
    304   if (on) (void)kit_writer_write(out, obuf, on);
    305 }
    306 
    307 /* ============================================================
    308  * SrcInfo registry — lazy loc + retained text
    309  * ============================================================ */
    310 
    311 SrcInfo* pp_srcinfo(Pp* pp, u32 file_id) {
    312   if (file_id >= pp->srcinfo_cap) {
    313     u32 nc = pp->srcinfo_cap ? pp->srcinfo_cap * 2 : 16;
    314     while (nc <= file_id) nc *= 2;
    315     pp->srcinfo = (SrcInfo*)pp_xrealloc(
    316         pp, pp->srcinfo, sizeof(SrcInfo) * pp->srcinfo_cap,
    317         sizeof(SrcInfo) * nc, _Alignof(SrcInfo));
    318     memset(pp->srcinfo + pp->srcinfo_cap, 0,
    319            sizeof(SrcInfo) * (nc - pp->srcinfo_cap));
    320     pp->srcinfo_cap = nc;
    321   }
    322   return &pp->srcinfo[file_id];
    323 }
    324 
    325 void pp_register_srcinfo(Pp* pp, Lexer* lex) {
    326   u32 fid = lex_file_id(lex);
    327   SrcInfo* si = pp_srcinfo(pp, fid);
    328   u32 nspl = 0;
    329   si->buf = lex_buf(lex);
    330   si->len = lex_buf_len(lex);
    331   si->owns_buf = (u8)lex_owns_buf(lex);
    332   si->splices = (u32*)lex_splices(lex, &nspl);
    333   si->nsplices = nspl;
    334   si->shebang_off = lex_shebang_off(lex);
    335   si->line_built = 0;
    336   si->line_off = NULL;
    337   si->nlines = 0;
    338   si->line_cursor = 0;
    339   si->segs = NULL;
    340   si->nsegs = 0;
    341   si->segs_cap = 0;
    342   /* PP now owns the buffer + splice table; the lexer must not free them. */
    343   lex_disown_buf(lex);
    344 }
    345 
    346 void pp_add_line_seg(Pp* pp, u32 file_id, u32 off, i32 delta, Sym file) {
    347   SrcInfo* si = pp_srcinfo(pp, file_id);
    348   if (si->nsegs == si->segs_cap) {
    349     u32 nc = si->segs_cap ? si->segs_cap * 2 : 4;
    350     si->segs =
    351         (LineSeg*)pp_xrealloc(pp, si->segs, sizeof(LineSeg) * si->segs_cap,
    352                               sizeof(LineSeg) * nc, _Alignof(LineSeg));
    353     si->segs_cap = nc;
    354   }
    355   si->segs[si->nsegs].off = off;
    356   si->segs[si->nsegs].delta = delta;
    357   si->segs[si->nsegs].file = file;
    358   ++si->nsegs;
    359 }
    360 
    361 /* Build the physical line-start index for a source by replaying the lexer's
    362  * line accounting over the (immutable, fully-folded) buffer. A line-break event
    363  * has a "trigger" offset equal to the line_start it establishes: a '\n' at
    364  * offset p triggers at p+1; a folded splice at offset s triggers at s. The
    365  * lexer processes both in scan order, so the sorted multiset of triggers,
    366  * prefixed by line 1's origin (shebang_off, else 0), reproduces line/col
    367  * exactly: line(off) = 1 + count(triggers <= off); col = off - origin + 1. */
    368 static void srcinfo_build_lines(Pp* pp, SrcInfo* si) {
    369   const char* b = si->buf;
    370   const char* e = b + si->len;
    371   const char* p;
    372   const char* nl;
    373   u32 nnl = 0, k, sp;
    374   u32* lo;
    375   /* Count newlines via memchr (NEON-accelerated) rather than a scalar byte
    376    * loop — this build is the one-time cost paid the first time a loc in this
    377    * source is materialized. */
    378   for (p = b; (nl = (const char*)memchr(p, '\n', (size_t)(e - p))) != NULL;)
    379     ++nnl, p = nl + 1;
    380   {
    381     u32 cap = 1u + nnl + si->nsplices;
    382     lo = (u32*)pp_xrealloc(pp, NULL, 0, sizeof(u32) * cap, _Alignof(u32));
    383   }
    384   k = 0;
    385   lo[k++] = si->shebang_off;
    386   sp = 0;
    387   for (p = b; (nl = (const char*)memchr(p, '\n', (size_t)(e - p))) != NULL;) {
    388     u32 nlv = (u32)(nl - b) + 1u;
    389     while (sp < si->nsplices && si->splices[sp] < nlv)
    390       lo[k++] = si->splices[sp++];
    391     lo[k++] = nlv;
    392     p = nl + 1;
    393   }
    394   while (sp < si->nsplices) lo[k++] = si->splices[sp++];
    395   si->line_off = lo;
    396   si->nlines = k;
    397   si->line_cursor = 0;
    398   si->line_built = 1;
    399 }
    400 
    401 /* Largest index k in line_off[0..n) with line_off[k] <= off (n >= 1). */
    402 static u32 line_index(const u32* lo, u32 n, u32 off) {
    403   u32 a = 0, b = n;
    404   while (a < b) {
    405     u32 m = a + (b - a) / 2u;
    406     if (lo[m] <= off)
    407       a = m + 1u;
    408     else
    409       b = m;
    410   }
    411   return a ? a - 1u : 0u;
    412 }
    413 
    414 /* Cursored line lookup: loc materialization walks the source in near-monotonic
    415  * byte order (the parser advances forward), so the previously-returned line is
    416  * almost always the answer or one step behind. Hit-in-line and short forward
    417  * runs are O(1); a far forward jump or any backward jump falls back to the
    418  * binary search. Updates the cursor for the next call. */
    419 static u32 line_index_cursored(SrcInfo* si, u32 off) {
    420   const u32* lo = si->line_off;
    421   u32 n = si->nlines;
    422   u32 c = si->line_cursor;
    423   if (c >= n) c = 0;
    424   if (lo[c] <= off) {
    425     if (c + 1u >= n || off < lo[c + 1u])
    426       return c; /* same line — the hot case */
    427     {
    428       u32 step = 0;
    429       while (c + 1u < n && lo[c + 1u] <= off && step < 8u) ++c, ++step;
    430       if (c + 1u < n && lo[c + 1u] <= off) c = line_index(lo, n, off);
    431     }
    432   } else {
    433     c = line_index(lo, n, off); /* backward jump */
    434   }
    435   si->line_cursor = c;
    436   return c;
    437 }
    438 
    439 static i32 seg_delta_at(const SrcInfo* si, u32 off) {
    440   i32 d = 0;
    441   u32 i;
    442   for (i = 0; i < si->nsegs; ++i) {
    443     if (si->segs[i].off <= off)
    444       d = si->segs[i].delta;
    445     else
    446       break;
    447   }
    448   return d;
    449 }
    450 
    451 static Sym seg_file_at(const SrcInfo* si, u32 off) {
    452   Sym f = 0;
    453   u32 i;
    454   for (i = 0; i < si->nsegs; ++i) {
    455     if (si->segs[i].off <= off)
    456       f = si->segs[i].file;
    457     else
    458       break;
    459   }
    460   return f;
    461 }
    462 
    463 SrcLoc pp_materialize_loc(Pp* pp, LocRef loc) {
    464   SrcLoc r;
    465   SrcInfo* si;
    466   u32 k;
    467   r.file_id = loc.file_id;
    468   r.line = 0;
    469   r.col = 0;
    470   if (!loc.file_id) return r;
    471   si = pp_srcinfo(pp, loc.file_id);
    472   if (!si->buf) return r;
    473   if (!si->line_built) srcinfo_build_lines(pp, si);
    474   k = line_index_cursored(si, loc.off);
    475   r.line = (u32)((i32)(k + 1u) + seg_delta_at(si, loc.off));
    476   r.col = loc.off - si->line_off[k] + 1u;
    477   return r;
    478 }
    479 
    480 u32 pp_phys_line(Pp* pp, LocRef loc) {
    481   SrcInfo* si;
    482   if (!loc.file_id) return 0;
    483   si = pp_srcinfo(pp, loc.file_id);
    484   if (!si->buf) return 0;
    485   if (!si->line_built) srcinfo_build_lines(pp, si);
    486   return line_index(si->line_off, si->nlines, loc.off) + 1u;
    487 }
    488 
    489 Sym pp_materialize_file(Pp* pp, LocRef loc) {
    490   SrcInfo* si;
    491   Sym f;
    492   KitSourceFile sf;
    493   if (!loc.file_id) return 0;
    494   si = pp_srcinfo(pp, loc.file_id);
    495   f = si->buf ? seg_file_at(si, loc.off) : 0;
    496   if (f) return f;
    497   if (kit_source_file(pp->c, loc.file_id, &sf) == KIT_OK) return sf.name;
    498   return 0;
    499 }
    500 
    501 /* ============================================================
    502  * Text materialization
    503  * ============================================================ */
    504 
    505 KitSlice pp_text_slice(Pp* pp, const Tok* t) {
    506   KitSlice s;
    507   switch (t->text.kind) {
    508     case TEXT_SRC: {
    509       SrcInfo* si = pp_srcinfo(pp, t->text.file_id);
    510       s.s = si->buf + t->text.off;
    511       s.len = t->text.len_or_sym;
    512       return s;
    513     }
    514     case TEXT_SYM:
    515       return kit_sym_str(pp->pool->c, (Sym)t->text.len_or_sym);
    516     default: /* TEXT_NONE: a canonical punctuator reconstructs from its code */
    517       if (t->kind == TOK_PUNCT || t->kind == TOK_PP_HASH ||
    518           t->kind == TOK_PP_PASTE) {
    519         u32 n;
    520         s.s = punct_canon(t->aux, &n);
    521         s.len = n;
    522         return s;
    523       }
    524       s.s = "";
    525       s.len = 0;
    526       return s;
    527   }
    528 }
    529 
    530 Sym pp_text_intern(Pp* pp, const Tok* t) {
    531   if (t->text.kind == TEXT_SYM) return (Sym)t->text.len_or_sym;
    532   return kit_sym_intern(pp->pool->c, pp_text_slice(pp, t));
    533 }
    534 
    535 int pp_text_eq_cstr(Pp* pp, const Tok* t, const char* s) {
    536   KitSlice sl = pp_text_slice(pp, t);
    537   size_t n = s ? strlen(s) : 0;
    538   return sl.len == n && (n == 0 || memcmp(sl.s, s, n) == 0);
    539 }
    540 
    541 /* ============================================================
    542  * Lifecycle and configuration
    543  * ============================================================ */
    544 
    545 static void pp_intern_keywords(Pp* pp) {
    546   Pool* p = pp->pool;
    547   pp->sym_define = kit_sym_intern(p->c, KIT_SLICE_LIT("define"));
    548   pp->sym_undef = kit_sym_intern(p->c, KIT_SLICE_LIT("undef"));
    549   pp->sym_include = kit_sym_intern(p->c, KIT_SLICE_LIT("include"));
    550   pp->sym_include_next = kit_sym_intern(p->c, KIT_SLICE_LIT("include_next"));
    551   pp->sym_assembler = kit_sym_intern(p->c, KIT_SLICE_LIT("__ASSEMBLER__"));
    552   pp->sym_has_include = kit_sym_intern(p->c, KIT_SLICE_LIT("__has_include"));
    553   pp->sym_has_include_next =
    554       kit_sym_intern(p->c, KIT_SLICE_LIT("__has_include_next"));
    555   pp->sym_if = kit_sym_intern(p->c, KIT_SLICE_LIT("if"));
    556   pp->sym_ifdef = kit_sym_intern(p->c, KIT_SLICE_LIT("ifdef"));
    557   pp->sym_ifndef = kit_sym_intern(p->c, KIT_SLICE_LIT("ifndef"));
    558   pp->sym_elif = kit_sym_intern(p->c, KIT_SLICE_LIT("elif"));
    559   pp->sym_else = kit_sym_intern(p->c, KIT_SLICE_LIT("else"));
    560   pp->sym_endif = kit_sym_intern(p->c, KIT_SLICE_LIT("endif"));
    561   pp->sym_line = kit_sym_intern(p->c, KIT_SLICE_LIT("line"));
    562   pp->sym_pragma = kit_sym_intern(p->c, KIT_SLICE_LIT("pragma"));
    563   pp->sym_once = kit_sym_intern(p->c, KIT_SLICE_LIT("once"));
    564   pp->sym_pragma_kw = pp->sym_pragma;
    565   pp->sym_error = kit_sym_intern(p->c, KIT_SLICE_LIT("error"));
    566   pp->sym_warning = kit_sym_intern(p->c, KIT_SLICE_LIT("warning"));
    567   pp->sym_embed = kit_sym_intern(p->c, KIT_SLICE_LIT("embed"));
    568   pp->sym_defined = kit_sym_intern(p->c, KIT_SLICE_LIT("defined"));
    569   pp->sym_va_args = kit_sym_intern(p->c, KIT_SLICE_LIT("__VA_ARGS__"));
    570   pp->sym_line__ = kit_sym_intern(p->c, KIT_SLICE_LIT("__LINE__"));
    571   pp->sym_file__ = kit_sym_intern(p->c, KIT_SLICE_LIT("__FILE__"));
    572   pp->sym_date__ = kit_sym_intern(p->c, KIT_SLICE_LIT("__DATE__"));
    573   pp->sym_time__ = kit_sym_intern(p->c, KIT_SLICE_LIT("__TIME__"));
    574   pp->sym_stdc__ = kit_sym_intern(p->c, KIT_SLICE_LIT("__STDC__"));
    575   pp->sym_stdc_hosted__ =
    576       kit_sym_intern(p->c, KIT_SLICE_LIT("__STDC_HOSTED__"));
    577   pp->sym_stdc_version__ =
    578       kit_sym_intern(p->c, KIT_SLICE_LIT("__STDC_VERSION__"));
    579   pp->sym__pragma = kit_sym_intern(p->c, KIT_SLICE_LIT("_Pragma"));
    580 }
    581 
    582 /* Decompose unix seconds into UTC y/M/d/h/m/s. Algorithm: Howard Hinnant,
    583  * "chrono-compatible Low-Level Date Algorithms" (civil_from_days). Valid
    584  * for any int64 input; uses floor division so negative epoch values
    585  * (pre-1970) work correctly. */
    586 typedef struct PpYMD {
    587   int y; /* full year, e.g. 2026 */
    588   int M; /* 1..12 */
    589   int d; /* 1..31 */
    590   int h; /* 0..23 */
    591   int m; /* 0..59 */
    592   int s; /* 0..59 */
    593 } PpYMD;
    594 
    595 static void pp_break_time(int64_t t, PpYMD* out) {
    596   int64_t days, secs;
    597   int64_t z, era, doe, yoe, y, doy, mp, d, mo;
    598   /* Floor-divide t by 86400. */
    599   days = t / 86400;
    600   secs = t - days * 86400;
    601   if (secs < 0) {
    602     secs += 86400;
    603     days -= 1;
    604   }
    605   out->h = (int)(secs / 3600);
    606   out->m = (int)((secs / 60) % 60);
    607   out->s = (int)(secs % 60);
    608 
    609   z = days + 719468; /* shift to era starting 0000-03-01 */
    610   era = (z >= 0 ? z : z - 146096) / 146097;
    611   doe = z - era * 146097;                                      /* [0,146096] */
    612   yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; /* [0,399] */
    613   y = yoe + era * 400;
    614   doy = doe - (365 * yoe + yoe / 4 - yoe / 100); /* [0,365] */
    615   mp = (5 * doy + 2) / 153;                      /* [0,11], Mar=0 */
    616   d = doy - (153 * mp + 2) / 5 + 1;              /* [1,31] */
    617   mo = mp + (mp < 10 ? 3 : -9);                  /* [1,12] */
    618   y += (mo <= 2);
    619   out->y = (int)y;
    620   out->M = (int)mo;
    621   out->d = (int)d;
    622 }
    623 
    624 /* Compute __DATE__ and __TIME__ from env->now (unix seconds, host-supplied;
    625  * negative means "no clock"). Per C11 §6.10.8.1: __DATE__ is "Mmm dd yyyy"
    626  * (dd is space-padded if < 10), __TIME__ is "hh:mm:ss". Both quoted. */
    627 static void compute_date_time(Pp* pp) {
    628   static const char* mons[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
    629                                "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
    630   char date[24];
    631   char tm[16];
    632   int64_t t = kit_compiler_context(pp->c)->now;
    633   PpYMD ymd;
    634   if (t < 0) {
    635     pp->val_date_str =
    636         kit_sym_intern(pp->pool->c, KIT_SLICE_LIT("\"??? ?? ????\""));
    637     pp->val_time_str =
    638         kit_sym_intern(pp->pool->c, KIT_SLICE_LIT("\"??:??:??\""));
    639     return;
    640   }
    641   pp_break_time(t, &ymd);
    642   {
    643     int dd = ymd.d, yyyy = ymd.y;
    644     int p = 0;
    645     date[p++] = '"';
    646     memcpy(date + p, mons[ymd.M - 1], 3);
    647     p += 3;
    648     date[p++] = ' ';
    649     date[p++] = (dd >= 10) ? (char)('0' + dd / 10) : ' ';
    650     date[p++] = (char)('0' + dd % 10);
    651     date[p++] = ' ';
    652     date[p++] = (char)('0' + (yyyy / 1000) % 10);
    653     date[p++] = (char)('0' + (yyyy / 100) % 10);
    654     date[p++] = (char)('0' + (yyyy / 10) % 10);
    655     date[p++] = (char)('0' + (yyyy) % 10);
    656     date[p++] = '"';
    657     pp->val_date_str =
    658         kit_sym_intern(pp->pool->c, (KitSlice){.s = date, .len = (size_t)p});
    659   }
    660   {
    661     int hh = ymd.h, mm = ymd.m, ss = ymd.s;
    662     int p = 0;
    663     tm[p++] = '"';
    664     tm[p++] = (char)('0' + (hh / 10) % 10);
    665     tm[p++] = (char)('0' + hh % 10);
    666     tm[p++] = ':';
    667     tm[p++] = (char)('0' + (mm / 10) % 10);
    668     tm[p++] = (char)('0' + mm % 10);
    669     tm[p++] = ':';
    670     tm[p++] = (char)('0' + (ss / 10) % 10);
    671     tm[p++] = (char)('0' + ss % 10);
    672     tm[p++] = '"';
    673     pp->val_time_str =
    674         kit_sym_intern(pp->pool->c, (KitSlice){.s = tm, .len = (size_t)p});
    675   }
    676 }
    677 
    678 static void pp_register_static_predefined(Pp* pp) {
    679   pp_define(pp, "__kit__", "1");
    680   pp_define(pp, "__kit_major__", "0");
    681   pp_define(pp, "__kit_minor__", "0");
    682   pp_define(pp, "__kit_patchlevel__", "0");
    683   pp_define(pp, "__STDC__", "1");
    684   pp_define(pp, "__STDC_HOSTED__", "0");
    685   pp_define(pp, "__STDC_VERSION__", "201112L");
    686   /* C11 memory_order constants used by __atomic_* builtins. Values match the
    687    * `MemOrder` enum in src/arch/arch.h so eval_const_int -> MemOrder is a
    688    * direct cast. */
    689   pp_define(pp, "__ATOMIC_RELAXED", "0");
    690   pp_define(pp, "__ATOMIC_CONSUME", "1");
    691   pp_define(pp, "__ATOMIC_ACQUIRE", "2");
    692   pp_define(pp, "__ATOMIC_RELEASE", "3");
    693   pp_define(pp, "__ATOMIC_ACQ_REL", "4");
    694   pp_define(pp, "__ATOMIC_SEQ_CST", "5");
    695   /* GNU `__extension__` is a pedantic-quiet prefix on non-standard constructs
    696    * (statement exprs, anonymous structs, `long long` in C89, ...). kit's parser
    697    * is permissive about those already and the keyword has no effect on parsing,
    698    * so erase it. Needed on every OS, not just mingw: glibc's headers (e.g.
    699    * <stdlib.h>'s `__extension__ typedef struct { ... } lldiv_t;`) use it
    700    * pervasively, and musl's cleaner ISO-C headers simply never tripped it. */
    701   pp_define(pp, "__extension__", "");
    702   /* GCC keyword spellings of the C qualifiers/`signed`. GNU libc and the Linux
    703    * kernel UAPI headers use these directly in GCC mode (e.g.
    704    * <asm-generic/int-ll64.h>'s `typedef __signed__ char __s8;`). kit parses the
    705    * canonical keywords; map the GCC spellings onto them. (`__restrict` is
    706    * additionally a parser keyword alias for the configs that #undef the macro.)
    707    */
    708   pp_define(pp, "__volatile__", "volatile");
    709   pp_define(pp, "__const__", "const");
    710   pp_define(pp, "__signed__", "signed");
    711 }
    712 
    713 /* OS-keyed predefined macros: the single place to extend per operating system.
    714  * Each OS arm owns its full set of OS-specific predefines (including the
    715  * arch-specific MSVC machine macros, which are an OS-flavor concern, not a
    716  * data-model one). Add a new `case` to support another OS personality; the
    717  * data-model / type-width macros stay in pp_register_target_predefined since
    718  * they are OS-independent. */
    719 static void pp_register_os_predefined(Pp* pp, KitTargetSpec target) {
    720   switch (target.os) {
    721     case KIT_OS_WINDOWS:
    722       /* Windows / mingw predefined macros. kit targets the mingw
    723        * flavor (DWARF debug info, mingwex CRT) rather than MSVC, so we
    724        * advertise __MINGW{32,64}__ and friends but never set _MSC_VER.
    725        * Both _WIN32 and the legacy unprefixed WIN32 are defined; _WIN64
    726        * is set on 64-bit targets only. The MSVC-compat machine macros
    727        * (_M_X64 / _M_AMD64 / _M_ARM64) are useful for headers that gate
    728        * on them but harmless to set everywhere — mingw's own headers
    729        * tolerate them. */
    730       pp_define(pp, "_WIN32", "1");
    731       pp_define(pp, "WIN32", "1");
    732       pp_define(pp, "__MINGW32__", "1");
    733       if (target.ptr_size == 8) {
    734         pp_define(pp, "_WIN64", "1");
    735         pp_define(pp, "__MINGW64__", "1");
    736       }
    737       if (target.arch == KIT_ARCH_X86_64) {
    738         pp_define(pp, "_M_X64", "100");
    739         pp_define(pp, "_M_AMD64", "100");
    740       } else if (target.arch == KIT_ARCH_ARM_64) {
    741         pp_define(pp, "_M_ARM64", "1");
    742       }
    743       /* mingw's <vadefs.h> / many CRT headers gate __builtin_va_list /
    744        * __gnuc_va_list on __GNUC__. kit implements the va_* builtins
    745        * and __builtin_va_list with the GCC contract, so impersonating a
    746        * conservative GCC vintage lets the mingw header tree compile.
    747        * We pick 4.0 — old enough that no header expects GCC-specific
    748        * extensions kit doesn't implement (e.g. transactional memory,
    749        * GIMPLE plugins), but new enough to clear every __GNUC__ >= N
    750        * gate we've seen in practice. */
    751       pp_define(pp, "__GNUC__", "4");
    752       pp_define(pp, "__GNUC_MINOR__", "0");
    753       pp_define(pp, "__GNUC_PATCHLEVEL__", "0");
    754       /* __has_builtin / __has_attribute / __has_include_next: clang/GCC
    755        * preprocessor extensions. mingw's _mingw.h gates inline-asm
    756        * intrinsic definitions on whether the compiler claims to have
    757        * them as builtins (e.g. __debugbreak, __fastfail, __prefetch).
    758        * kit doesn't model individual builtin lookups; claim "yes"
    759        * uniformly so mingw skips its inline-asm fallbacks (which use
    760        * intel/{$}-form asm syntax kit's parser doesn't accept). */
    761       pp_define(pp, "__has_builtin(x)", "1");
    762       pp_define(pp, "__has_feature(x)", "0");
    763       pp_define(pp, "__has_attribute(x)", "0");
    764       /* MSVC fixed-width integer types. mingw's corecrt.h uses these
    765        * directly (e.g. `typedef unsigned __int64 size_t;`). Map to the
    766        * C standard equivalents. */
    767       pp_define(pp, "__int8", "char");
    768       pp_define(pp, "__int16", "short");
    769       pp_define(pp, "__int32", "int");
    770       pp_define(pp, "__int64", "long long");
    771       /* mingw's psdk_inc/intrin-impl.h emits an inline implementation
    772        * for every MSVC intrinsic (_lrotl, _BitScanForward, ...) and
    773        * gates them with __INTRINSIC_PROLOG, which uses ## to paste the
    774        * intrinsic's name into a `defined(__INTRINSIC_DEFINED_<name>)`
    775        * test. Once an intrinsic gets defined, a later re-invocation of
    776        * the same gate macro hits a kit pp bug where a *defined*
    777        * symbol referenced inside `defined()` gets expanded before the
    778        * `defined` operator captures it. Predefining
    779        * __INTRINSIC_ONLYSPECIAL flips the gate's second clause so
    780        * none of the inline intrinsics are emitted (mingw expects this
    781        * idiom for non-special builds; the linker pulls them from
    782        * libmingwex/libmsvcrt instead). This sidesteps the pp bug
    783        * entirely. */
    784       pp_define(pp, "__INTRINSIC_ONLYSPECIAL", "1");
    785       /* __declspec(...) is the MSVC syntax for attributes. mingw uses
    786        * it in headers for dllimport/dllexport, alignment, noreturn,
    787        * etc. kit's COFF linker routes externs through the IAT
    788        * regardless of the dllimport hint and doesn't yet model
    789        * dllexport via this attribute — so we erase it as a no-op
    790        * macro. (Note: this is at the preprocessor layer; the parser
    791        * still needs to handle the syntax if/when the macro is removed.)
    792        */
    793       pp_define(pp, "__declspec(x)", "");
    794       /* __extension__ is erased unconditionally in
    795        * pp_register_static_predefined. */
    796       /* __restrict / __restrict__: GCC-flavored alternates to the C99
    797        * `restrict` keyword. kit parses `restrict` already; map the
    798        * GCC spellings onto it. */
    799       pp_define(pp, "__restrict", "restrict");
    800       pp_define(pp, "__restrict__", "restrict");
    801       /* __volatile__/__const__/__signed__ erased->canonical unconditionally in
    802        * pp_register_static_predefined (GNU spellings glibc/UAPI headers use).
    803        */
    804       /* MSVC calling-convention attributes. On x86_64 they're no-ops
    805        * (every function uses the Win64 ABI) and on ARM64 likewise; on
    806        * i386 they actually mean something but kit doesn't target it.
    807        * Defining them as empty macros lets mingw headers that say
    808        * `void __cdecl foo(void)` parse correctly. Same posture mingw's
    809        * own GCC takes: __MINGW_USYMBOL((__cdecl__)). */
    810       /* MSVC calling-convention attributes — no-ops on Win64. kit
    811        * pre-defines them empty *only when* mingw's headers don't
    812        * themselves redefine them; we use the __MINGW_<x>_REDEFINE form
    813        * via `#undef` first to play nicely with mingw's own
    814        * redefinitions (mingw's _mingw.h does `#define __cdecl
    815        * __attribute__((__cdecl__))` further down). Setting them empty
    816        * here is safe because kit's parser will see the redefinition
    817        * before any header uses them. */
    818       pp_define(pp, "__cdecl", "");
    819       pp_define(pp, "__stdcall", "");
    820       pp_define(pp, "__fastcall", "");
    821       pp_define(pp, "__thiscall", "");
    822       pp_define(pp, "__vectorcall", "");
    823       pp_define(pp, "_cdecl", "");
    824       pp_define(pp, "_stdcall", "");
    825       pp_define(pp, "_fastcall", "");
    826       /* __forceinline / __inline / __w64: mingw's _mingw.h redefines
    827        * them itself when __GNUC__ is set, so we leave them alone here
    828        * to avoid a redefinition-with-different-replacement error. */
    829       break;
    830     case KIT_OS_LINUX:
    831     case KIT_OS_ANDROID:
    832     case KIT_OS_MACOS:
    833     case KIT_OS_IOS:
    834     case KIT_OS_IOS_SIMULATOR:
    835     case KIT_OS_FREEBSD:
    836     case KIT_OS_FREESTANDING:
    837     case KIT_OS_WASI:
    838       /* No OS-specific predefines beyond the shared data-model set. */
    839       break;
    840   }
    841 }
    842 
    843 /* Target-dependent predefined macros consumed by rt/include/stddef.h and
    844  * rt/include/stdint.h. The set mirrors the subset of GCC/Clang's __*_TYPE__
    845  * / __*_MAX__ namespace that those headers reference. We split only on
    846  * pointer width plus the target data model: LP64 for Unix-like 64-bit targets,
    847  * LLP64 for 64-bit Windows, and ILP32 for 32-bit targets. */
    848 static void pp_register_target_predefined(Pp* pp) {
    849   KitTargetSpec target = kit_compiler_target_spec(pp->c);
    850   const KitPredefinedMacro* arch_defs = NULL;
    851   uint32_t narch_defs = kit_compiler_arch_predefines(pp->c, &arch_defs);
    852   uint32_t i;
    853   int ptr64 = (target.ptr_size == 8);
    854   int lp64 = kit_target_uses_lp64(target);
    855   /* sizeof(wchar_t) is a resolved data-model fact carried on the spec. */
    856   int wchar16 = (target.wchar_size == 2);
    857 
    858   for (i = 0; i < narch_defs; ++i) {
    859     pp_define(pp, arch_defs[i].name.s, arch_defs[i].body.s);
    860   }
    861 
    862   /* Float-ABI-dependent arch macros (e.g. RISC-V __riscv_float_abi_* /
    863    * __riscv_flen) come from a separate getter because the static table above
    864    * can encode only one float profile; these track the resolved float ABI. */
    865   {
    866     const KitPredefinedMacro* fp_defs = NULL;
    867     uint32_t nfp_defs = kit_compiler_arch_float_predefines(pp->c, &fp_defs);
    868     for (i = 0; i < nfp_defs; ++i) {
    869       pp_define(pp, fp_defs[i].name.s, fp_defs[i].body.s);
    870     }
    871   }
    872 
    873   /* Feature-keyed arch macros (e.g. ARM's __ARM_ARCH_7M__ vs __ARM_ARCH_7EM__,
    874    * __ARM_FEATURE_DSP) come from a separate getter because the static table
    875    * above can encode only one extension profile; these track the resolved
    876    * -march/-mcpu/-mattr feature words. */
    877   {
    878     const KitPredefinedMacro* feat_defs = NULL;
    879     uint32_t nfeat_defs = kit_compiler_arch_feature_predefines(pp->c, &feat_defs);
    880     for (i = 0; i < nfeat_defs; ++i) {
    881       pp_define(pp, feat_defs[i].name.s, feat_defs[i].body.s);
    882     }
    883   }
    884 
    885   /* __USER_LABEL_PREFIX__ is the C source-symbol prefix the object format
    886    * prepends ("_" for Mach-O, "" else); read it from the CG target rather
    887    * than re-deriving from the object-format identity. */
    888   pp_define(pp, "__USER_LABEL_PREFIX__", kit_cg_target_c_label_prefix(pp->c));
    889 
    890   /* Byte / type sizes. kit uses a single LP64 (or ILP32) model across
    891    * every supported target: int=4, short=2, long-long=8, float=4, double=8,
    892    * long-double=8 (sharing the double representation — see the
    893    * __LDBL_* block below).  long and pointer-derived types track ptr_size.
    894    * These macros let portable C code probe widths without first pulling in
    895    * <limits.h> / <stddef.h>. */
    896   pp_define(pp, "__CHAR_BIT__", "8");
    897   pp_define(pp, "__SIZEOF_SHORT__", "2");
    898   pp_define(pp, "__SIZEOF_INT__", "4");
    899   pp_define(pp, "__SIZEOF_LONG__", lp64 ? "8" : "4");
    900   pp_define(pp, "__SIZEOF_LONG_LONG__", "8");
    901   pp_define(pp, "__SIZEOF_POINTER__", ptr64 ? "8" : "4");
    902   pp_define(pp, "__SIZEOF_SIZE_T__", ptr64 ? "8" : "4");
    903   pp_define(pp, "__SIZEOF_PTRDIFF_T__", ptr64 ? "8" : "4");
    904   pp_define(pp, "__SIZEOF_WCHAR_T__", wchar16 ? "2" : "4");
    905   pp_define(pp, "__SIZEOF_WINT_T__", "4");
    906   pp_define(pp, "__SIZEOF_FLOAT__", "4");
    907   pp_define(pp, "__SIZEOF_DOUBLE__", "8");
    908   pp_define(pp, "__SIZEOF_LONG_DOUBLE__",
    909             kit_target_long_double_is_binary128(target) ? "16" : "8");
    910 
    911   /* OS-specific predefines (Windows/mingw + MSVC machine macros) live in one
    912    * os-keyed table — pp_register_os_predefined — so adding an OS personality
    913    * touches a single place. Emitted here, between the data-model size macros
    914    * and the stddef.h type aliases, to preserve the predefined-macro ordering.
    915    */
    916   pp_register_os_predefined(pp, target);
    917 
    918   /* stddef.h base aliases */
    919   if (lp64) {
    920     pp_define(pp, "__SIZE_TYPE__", "unsigned long");
    921     pp_define(pp, "__PTRDIFF_TYPE__", "long");
    922   } else if (ptr64) {
    923     pp_define(pp, "__SIZE_TYPE__", "unsigned long long");
    924     pp_define(pp, "__PTRDIFF_TYPE__", "long long");
    925   } else {
    926     pp_define(pp, "__SIZE_TYPE__", "unsigned int");
    927     pp_define(pp, "__PTRDIFF_TYPE__", "int");
    928   }
    929   pp_define(pp, "__WCHAR_TYPE__", wchar16 ? "unsigned short" : "int");
    930   pp_define(pp, "__CHAR16_TYPE__", "unsigned short");
    931   pp_define(pp, "__CHAR32_TYPE__", "unsigned int");
    932 
    933   /* stdint.h exact-width aliases (widths <= 32 are model-independent) */
    934   pp_define(pp, "__INT8_TYPE__", "signed char");
    935   pp_define(pp, "__INT16_TYPE__", "short");
    936   pp_define(pp, "__INT32_TYPE__", "int");
    937   pp_define(pp, "__UINT8_TYPE__", "unsigned char");
    938   pp_define(pp, "__UINT16_TYPE__", "unsigned short");
    939   pp_define(pp, "__UINT32_TYPE__", "unsigned int");
    940   pp_define(pp, "__INT64_TYPE__", lp64 ? "long" : "long long");
    941   pp_define(pp, "__UINT64_TYPE__",
    942             lp64 ? "unsigned long" : "unsigned long long");
    943 
    944   /* Least-width == exact-width on every target kit knows about */
    945   pp_define(pp, "__INT_LEAST8_TYPE__", "signed char");
    946   pp_define(pp, "__INT_LEAST16_TYPE__", "short");
    947   pp_define(pp, "__INT_LEAST32_TYPE__", "int");
    948   pp_define(pp, "__UINT_LEAST8_TYPE__", "unsigned char");
    949   pp_define(pp, "__UINT_LEAST16_TYPE__", "unsigned short");
    950   pp_define(pp, "__UINT_LEAST32_TYPE__", "unsigned int");
    951   pp_define(pp, "__INT_LEAST64_TYPE__", lp64 ? "long" : "long long");
    952   pp_define(pp, "__UINT_LEAST64_TYPE__",
    953             lp64 ? "unsigned long" : "unsigned long long");
    954 
    955   /* Fast types: fast8 stays at `signed char`; fast16/32/64 widen to the
    956    * register-width integer so the operation fits in a single instruction. */
    957   pp_define(pp, "__INT_FAST8_TYPE__", "signed char");
    958   pp_define(pp, "__UINT_FAST8_TYPE__", "unsigned char");
    959   pp_define(pp, "__INT_FAST8_MAX__", "127");
    960   pp_define(pp, "__UINT_FAST8_MAX__", "255");
    961   if (ptr64) {
    962     pp_define(pp, "__INT_FAST16_TYPE__", lp64 ? "long" : "long long");
    963     pp_define(pp, "__INT_FAST32_TYPE__", lp64 ? "long" : "long long");
    964     pp_define(pp, "__INT_FAST64_TYPE__", lp64 ? "long" : "long long");
    965     pp_define(pp, "__UINT_FAST16_TYPE__",
    966               lp64 ? "unsigned long" : "unsigned long long");
    967     pp_define(pp, "__UINT_FAST32_TYPE__",
    968               lp64 ? "unsigned long" : "unsigned long long");
    969     pp_define(pp, "__UINT_FAST64_TYPE__",
    970               lp64 ? "unsigned long" : "unsigned long long");
    971     pp_define(pp, "__INT_FAST16_MAX__",
    972               lp64 ? "9223372036854775807L" : "9223372036854775807LL");
    973     pp_define(pp, "__INT_FAST32_MAX__",
    974               lp64 ? "9223372036854775807L" : "9223372036854775807LL");
    975     pp_define(pp, "__INT_FAST64_MAX__",
    976               lp64 ? "9223372036854775807L" : "9223372036854775807LL");
    977     pp_define(pp, "__UINT_FAST16_MAX__",
    978               lp64 ? "18446744073709551615UL" : "18446744073709551615ULL");
    979     pp_define(pp, "__UINT_FAST32_MAX__",
    980               lp64 ? "18446744073709551615UL" : "18446744073709551615ULL");
    981     pp_define(pp, "__UINT_FAST64_MAX__",
    982               lp64 ? "18446744073709551615UL" : "18446744073709551615ULL");
    983   } else {
    984     pp_define(pp, "__INT_FAST16_TYPE__", "int");
    985     pp_define(pp, "__INT_FAST32_TYPE__", "int");
    986     pp_define(pp, "__INT_FAST64_TYPE__", "long long");
    987     pp_define(pp, "__UINT_FAST16_TYPE__", "unsigned int");
    988     pp_define(pp, "__UINT_FAST32_TYPE__", "unsigned int");
    989     pp_define(pp, "__UINT_FAST64_TYPE__", "unsigned long long");
    990     pp_define(pp, "__INT_FAST16_MAX__", "2147483647");
    991     pp_define(pp, "__INT_FAST32_MAX__", "2147483647");
    992     pp_define(pp, "__INT_FAST64_MAX__", "9223372036854775807LL");
    993     pp_define(pp, "__UINT_FAST16_MAX__", "4294967295U");
    994     pp_define(pp, "__UINT_FAST32_MAX__", "4294967295U");
    995     pp_define(pp, "__UINT_FAST64_MAX__", "18446744073709551615ULL");
    996   }
    997 
    998   /* Pointer-holding integers + ptrdiff/size maxes */
    999   if (lp64) {
   1000     pp_define(pp, "__LONG_MAX__", "9223372036854775807L");
   1001     pp_define(pp, "__INTPTR_TYPE__", "long");
   1002     pp_define(pp, "__UINTPTR_TYPE__", "unsigned long");
   1003     pp_define(pp, "__INTPTR_MAX__", "9223372036854775807L");
   1004     pp_define(pp, "__UINTPTR_MAX__", "18446744073709551615UL");
   1005     pp_define(pp, "__PTRDIFF_MAX__", "9223372036854775807L");
   1006     pp_define(pp, "__SIZE_MAX__", "18446744073709551615UL");
   1007   } else if (ptr64) {
   1008     pp_define(pp, "__LONG_MAX__", "2147483647L");
   1009     pp_define(pp, "__INTPTR_TYPE__", "long long");
   1010     pp_define(pp, "__UINTPTR_TYPE__", "unsigned long long");
   1011     pp_define(pp, "__INTPTR_MAX__", "9223372036854775807LL");
   1012     pp_define(pp, "__UINTPTR_MAX__", "18446744073709551615ULL");
   1013     pp_define(pp, "__PTRDIFF_MAX__", "9223372036854775807LL");
   1014     pp_define(pp, "__SIZE_MAX__", "18446744073709551615ULL");
   1015   } else {
   1016     pp_define(pp, "__LONG_MAX__", "2147483647L");
   1017     pp_define(pp, "__INTPTR_TYPE__", "int");
   1018     pp_define(pp, "__UINTPTR_TYPE__", "unsigned int");
   1019     pp_define(pp, "__INTPTR_MAX__", "2147483647");
   1020     pp_define(pp, "__UINTPTR_MAX__", "4294967295U");
   1021     pp_define(pp, "__PTRDIFF_MAX__", "2147483647");
   1022     pp_define(pp, "__SIZE_MAX__", "4294967295U");
   1023   }
   1024 
   1025   /* Greatest-width integers + matching _C() suffix macros */
   1026   if (lp64) {
   1027     pp_define(pp, "__INTMAX_TYPE__", "long");
   1028     pp_define(pp, "__UINTMAX_TYPE__", "unsigned long");
   1029     pp_define(pp, "__INTMAX_MAX__", "9223372036854775807L");
   1030     pp_define(pp, "__UINTMAX_MAX__", "18446744073709551615UL");
   1031     pp_define(pp, "__INT64_C(c)", "c ## L");
   1032     pp_define(pp, "__UINT64_C(c)", "c ## UL");
   1033     pp_define(pp, "__INTMAX_C(c)", "c ## L");
   1034     pp_define(pp, "__UINTMAX_C(c)", "c ## UL");
   1035     /* Suffix tokens (the form <stdint.h> uses to build INT64_C/etc.). GCC and
   1036      * clang predefine these; kit must too so kit-compiled TUs that include its
   1037      * freestanding <stdint.h> get the right-typed 64-bit constants. */
   1038     pp_define(pp, "__INT64_C_SUFFIX__", "L");
   1039     pp_define(pp, "__UINT64_C_SUFFIX__", "UL");
   1040     pp_define(pp, "__INTMAX_C_SUFFIX__", "L");
   1041     pp_define(pp, "__UINTMAX_C_SUFFIX__", "UL");
   1042   } else {
   1043     pp_define(pp, "__INTMAX_TYPE__", "long long");
   1044     pp_define(pp, "__UINTMAX_TYPE__", "unsigned long long");
   1045     pp_define(pp, "__INTMAX_MAX__", "9223372036854775807LL");
   1046     pp_define(pp, "__UINTMAX_MAX__", "18446744073709551615ULL");
   1047     pp_define(pp, "__INT64_C(c)", "c ## LL");
   1048     pp_define(pp, "__UINT64_C(c)", "c ## ULL");
   1049     pp_define(pp, "__INTMAX_C(c)", "c ## LL");
   1050     pp_define(pp, "__UINTMAX_C(c)", "c ## ULL");
   1051     pp_define(pp, "__INT64_C_SUFFIX__", "LL");
   1052     pp_define(pp, "__UINT64_C_SUFFIX__", "ULL");
   1053     pp_define(pp, "__INTMAX_C_SUFFIX__", "LL");
   1054     pp_define(pp, "__UINTMAX_C_SUFFIX__", "ULL");
   1055   }
   1056 
   1057   pp_define(pp, "__WCHAR_MAX__", wchar16 ? "65535" : "2147483647");
   1058   pp_define(pp, "__WCHAR_MIN__", wchar16 ? "0" : "(-__WCHAR_MAX__ - 1)");
   1059   pp_define(pp, "__WINT_MAX__", "2147483647");
   1060   pp_define(pp, "__WINT_MIN__", "(-__WINT_MAX__ - 1)");
   1061   pp_define(pp, "__SIG_ATOMIC_MAX__", "2147483647");
   1062   pp_define(pp, "__SIG_ATOMIC_MIN__", "(-__SIG_ATOMIC_MAX__ - 1)");
   1063 
   1064   /* C11 <stdatomic.h> lock-free macros. The currently supported primary
   1065    * targets have naturally lock-free scalar and pointer atomics through the
   1066    * machine-word sizes used by these typedefs. */
   1067   pp_define(pp, "__ATOMIC_BOOL_LOCK_FREE", "2");
   1068   pp_define(pp, "__ATOMIC_CHAR_LOCK_FREE", "2");
   1069   pp_define(pp, "__ATOMIC_CHAR16_T_LOCK_FREE", "2");
   1070   pp_define(pp, "__ATOMIC_CHAR32_T_LOCK_FREE", "2");
   1071   pp_define(pp, "__ATOMIC_WCHAR_T_LOCK_FREE", "2");
   1072   pp_define(pp, "__ATOMIC_SHORT_LOCK_FREE", "2");
   1073   pp_define(pp, "__ATOMIC_INT_LOCK_FREE", "2");
   1074   pp_define(pp, "__ATOMIC_LONG_LOCK_FREE", "2");
   1075   pp_define(pp, "__ATOMIC_LLONG_LOCK_FREE", "2");
   1076   pp_define(pp, "__ATOMIC_POINTER_LOCK_FREE", "2");
   1077 
   1078   pp_define(pp, "__FLT_EVAL_METHOD__", "0");
   1079   pp_define(pp, "__FLT_HAS_DENORM__", "1");
   1080   pp_define(pp, "__FLT_MANT_DIG__", "24");
   1081   pp_define(pp, "__FLT_DECIMAL_DIG__", "9");
   1082   pp_define(pp, "__FLT_DIG__", "6");
   1083   pp_define(pp, "__FLT_MIN_EXP__", "(-125)");
   1084   pp_define(pp, "__FLT_MIN_10_EXP__", "(-37)");
   1085   pp_define(pp, "__FLT_MAX_EXP__", "128");
   1086   pp_define(pp, "__FLT_MAX_10_EXP__", "38");
   1087   pp_define(pp, "__FLT_MAX__", "0x1.fffffep+127F");
   1088   pp_define(pp, "__FLT_EPSILON__", "0x1p-23F");
   1089   pp_define(pp, "__FLT_MIN__", "0x1p-126F");
   1090   pp_define(pp, "__FLT_DENORM_MIN__", "0x1p-149F");
   1091 
   1092   pp_define(pp, "__DBL_HAS_DENORM__", "1");
   1093   pp_define(pp, "__DBL_MANT_DIG__", "53");
   1094   pp_define(pp, "__DBL_DECIMAL_DIG__", "17");
   1095   pp_define(pp, "__DBL_DIG__", "15");
   1096   pp_define(pp, "__DBL_MIN_EXP__", "(-1021)");
   1097   pp_define(pp, "__DBL_MIN_10_EXP__", "(-307)");
   1098   pp_define(pp, "__DBL_MAX_EXP__", "1024");
   1099   pp_define(pp, "__DBL_MAX_10_EXP__", "308");
   1100   pp_define(pp, "__DBL_MAX__", "0x1.fffffffffffffp+1023");
   1101   pp_define(pp, "__DBL_EPSILON__", "0x1p-52");
   1102   pp_define(pp, "__DBL_MIN__", "0x1p-1022");
   1103   pp_define(pp, "__DBL_DENORM_MIN__", "0x1p-1074");
   1104 
   1105   /* Targets that follow the IEEE-754 binary128 quad psABI for `long double`
   1106    * (RISC-V, aarch64-linux, wasm32) get the 113-bit-mantissa characteristics;
   1107    * everything else aliases `double`. The wasm backend still reports f128 as
   1108    * unsupported when a value is actually materialized. See
   1109    * kit_target_long_double_is_binary128. */
   1110   if (kit_target_long_double_is_binary128(target)) {
   1111     pp_define(pp, "__LDBL_HAS_DENORM__", "1");
   1112     pp_define(pp, "__LDBL_MANT_DIG__", "113");
   1113     pp_define(pp, "__LDBL_DECIMAL_DIG__", "36");
   1114     pp_define(pp, "__LDBL_DIG__", "33");
   1115     pp_define(pp, "__LDBL_MIN_EXP__", "(-16381)");
   1116     pp_define(pp, "__LDBL_MIN_10_EXP__", "(-4931)");
   1117     pp_define(pp, "__LDBL_MAX_EXP__", "16384");
   1118     pp_define(pp, "__LDBL_MAX_10_EXP__", "4932");
   1119     pp_define(pp, "__LDBL_MAX__", "0x1.ffffffffffffffffffffffffffffp+16383L");
   1120     pp_define(pp, "__LDBL_EPSILON__", "0x1p-112L");
   1121     pp_define(pp, "__LDBL_MIN__", "0x1p-16382L");
   1122     pp_define(pp, "__LDBL_DENORM_MIN__", "0x1p-16494L");
   1123     pp_define(pp, "__DECIMAL_DIG__", "36");
   1124   } else {
   1125     pp_define(pp, "__LDBL_HAS_DENORM__", "1");
   1126     pp_define(pp, "__LDBL_MANT_DIG__", "53");
   1127     pp_define(pp, "__LDBL_DECIMAL_DIG__", "17");
   1128     pp_define(pp, "__LDBL_DIG__", "15");
   1129     pp_define(pp, "__LDBL_MIN_EXP__", "(-1021)");
   1130     pp_define(pp, "__LDBL_MIN_10_EXP__", "(-307)");
   1131     pp_define(pp, "__LDBL_MAX_EXP__", "1024");
   1132     pp_define(pp, "__LDBL_MAX_10_EXP__", "308");
   1133     pp_define(pp, "__LDBL_MAX__", "0x1.fffffffffffffp+1023L");
   1134     pp_define(pp, "__LDBL_EPSILON__", "0x1p-52L");
   1135     pp_define(pp, "__LDBL_MIN__", "0x1p-1022L");
   1136     pp_define(pp, "__LDBL_DENORM_MIN__", "0x1p-1074L");
   1137     pp_define(pp, "__DECIMAL_DIG__", "17");
   1138   }
   1139 }
   1140 
   1141 Pp* pp_new(Compiler* c) {
   1142   Heap* h = (Heap*)kit_compiler_context(c)->heap;
   1143   Pp* pp = (Pp*)h->alloc(h, sizeof(*pp), _Alignof(Pp));
   1144   if (!pp) return NULL;
   1145   memset(pp, 0, sizeof(*pp));
   1146   pp->c = c;
   1147   pp->pool = c_pool_new(c);
   1148   pp->arena = NULL;
   1149   pp->xarena = NULL;
   1150   (void)kit_arena_new(h, 64 * 1024, &pp->arena);
   1151   (void)kit_arena_new(h, 64 * 1024, &pp->xarena);
   1152   if (!pp->pool || !pp->arena || !pp->xarena) {
   1153     c_pool_free(pp->pool);
   1154     kit_arena_free(pp->arena);
   1155     kit_arena_free(pp->xarena);
   1156     h->free(h, pp, sizeof(*pp));
   1157     return NULL;
   1158   }
   1159   MacroTab_init(&pp->macros, h);
   1160   pp_intern_keywords(pp);
   1161   compute_date_time(pp);
   1162   pp_register_static_predefined(pp);
   1163   pp_register_target_predefined(pp);
   1164   return pp;
   1165 }
   1166 
   1167 void pp_free(Pp* pp) {
   1168   Heap* h;
   1169   if (!pp) return;
   1170   h = pp_heap(pp);
   1171   /* Pop / close any remaining lex sources. */
   1172   while (pp->nsources) src_pop(pp);
   1173   /* The reused token-paste lexer is never pushed as a source, so close it
   1174    * directly here (it is lazily opened on the first `##`). */
   1175   if (pp->paste_lex) lex_close(pp->paste_lex);
   1176   /* Release the SrcInfo registry: each entry's folded buffer (if PP owns it),
   1177    * splice table, lazy line index, and #line overlay segments. */
   1178   if (pp->srcinfo) {
   1179     u32 i;
   1180     for (i = 0; i < pp->srcinfo_cap; ++i) {
   1181       SrcInfo* si = &pp->srcinfo[i];
   1182       if (si->owns_buf && si->buf) pp_xfree(pp, (char*)si->buf, si->len);
   1183       if (si->splices) pp_xfree(pp, si->splices, si->nsplices * sizeof(u32));
   1184       if (si->line_off) pp_xfree(pp, si->line_off, si->nlines * sizeof(u32));
   1185       if (si->segs) pp_xfree(pp, si->segs, si->segs_cap * sizeof(LineSeg));
   1186     }
   1187     pp_xfree(pp, pp->srcinfo, sizeof(SrcInfo) * pp->srcinfo_cap);
   1188   }
   1189   pp_xfree(pp, pp->sources, sizeof(TokSrc) * pp->sources_cap);
   1190   MacroTab_fini(&pp->macros);
   1191   pp_xfree(pp, pp->ifstk, sizeof(IfFrame) * pp->ifstk_cap);
   1192   pp_xfree(pp, pp->inc_dirs, sizeof(*pp->inc_dirs) * pp->inc_dirs_cap);
   1193   c_pool_free(pp->pool);
   1194   kit_arena_free(pp->arena);
   1195   kit_arena_free(pp->xarena);
   1196   h->free(h, pp, sizeof(*pp));
   1197 }
   1198 
   1199 void pp_push_source(Pp* pp, const SourceSpec* spec) {
   1200   TokSrc s;
   1201   Lexer* lex = lex_open(pp->c, spec);
   1202   if (!lex) compiler_panic(pp->c, (SrcLoc){0, 0, 0}, "pp: out of memory");
   1203   if (spec->flags & SRC_PRIMARY) lex_skip_shebang(lex);
   1204   /* Remember parser-feed mode so #include'd lexers inherit it (they build their
   1205    * own SourceSpec in do_include and OR in SRC_PARSER_FEED from this flag). */
   1206   if (spec->flags & SRC_PARSER_FEED) pp->parser_feed = 1;
   1207   /* Adopt the source buffer + splice table for lazy loc/text materialization.
   1208    */
   1209   pp_register_srcinfo(pp, lex);
   1210   memset(&s, 0, sizeof(s));
   1211   s.kind = SRC_LEX;
   1212   s.lex = lex;
   1213   src_push(pp, s);
   1214 }
   1215 
   1216 void pp_add_include_dir(Pp* pp, const char* dir, int system) {
   1217   if (pp->ninc_dirs == pp->inc_dirs_cap) {
   1218     u32 nc = pp->inc_dirs_cap ? pp->inc_dirs_cap * 2 : 4;
   1219     pp->inc_dirs =
   1220         pp_xrealloc(pp, pp->inc_dirs, sizeof(*pp->inc_dirs) * pp->inc_dirs_cap,
   1221                     sizeof(*pp->inc_dirs) * nc, _Alignof(void*));
   1222     pp->inc_dirs_cap = nc;
   1223   }
   1224   pp->inc_dirs[pp->ninc_dirs].path = dir;
   1225   pp->inc_dirs[pp->ninc_dirs].system = (u8)(system ? 1 : 0);
   1226   ++pp->ninc_dirs;
   1227 }
   1228 
   1229 void pp_define(Pp* pp, const char* name, const char* body) {
   1230   /* Build a synthetic source line "name body\n" and run it through the lexer +
   1231    * define machinery so command-line -D matches the normal #define path. The
   1232    * buffer is allocated in pp->arena (retained to pp_free) because the
   1233    * resulting macro-body tokens carry TEXT_SRC spans into it. */
   1234   size_t nlen = name ? kit_slice_cstr(name).len : 0;
   1235   size_t blen = body ? kit_slice_cstr(body).len : 0;
   1236   char* buf;
   1237   size_t pos = 0;
   1238   SourceSpec spec;
   1239   TokSrc s;
   1240   Lexer* lex;
   1241   Tok* line;
   1242   u32 lineN;
   1243 
   1244   if (!name || !*name) return;
   1245   /* "name" + " " + "body" + "\n" */
   1246   buf = (char*)arena_alloc(pp->arena, nlen + 1 + blen + 1, 1);
   1247   memcpy(buf + pos, name, nlen);
   1248   pos += nlen;
   1249   buf[pos++] = ' ';
   1250   if (blen) {
   1251     memcpy(buf + pos, body, blen);
   1252     pos += blen;
   1253   }
   1254   buf[pos++] = '\n';
   1255 
   1256   memset(&spec, 0, sizeof(spec));
   1257   spec.name = KIT_SLICE_LIT("<command-line>");
   1258   spec.bytes = buf;
   1259   spec.len = (u32)pos;
   1260   lex = lex_open(pp->c, &spec);
   1261   pp_register_srcinfo(pp, lex);
   1262   memset(&s, 0, sizeof(s));
   1263   s.kind = SRC_LEX;
   1264   s.lex = lex;
   1265   src_push(pp, s);
   1266   read_directive_line(pp, &line, &lineN);
   1267   do_define(pp, line, lineN);
   1268   /* Drain anything trailing (shouldn't be any) and pop the lexer. */
   1269   src_pop(pp);
   1270 }
   1271 
   1272 void pp_undef(Pp* pp, const char* name) {
   1273   Sym s;
   1274   if (!name || !*name) return;
   1275   s = kit_sym_intern(pp->pool->c, kit_slice_cstr(name));
   1276   mt_del(pp, s);
   1277 }
   1278 
   1279 uint32_t pp_pack_alignment(const Pp* pp) { return pp ? pp->pack_align : 0; }
   1280 
   1281 void pp_add_include_edge(Pp* pp, u32 includer, u32 included, LocRef include_loc,
   1282                          int system) {
   1283   /* This generic edge-recording entry point has no resolved-dir context, so it
   1284    * can only fall back to the spelling form for the resolved-system flag. */
   1285   kit_source_add_include(pp->c, includer, included,
   1286                          pp_materialize_loc(pp, include_loc), system, system);
   1287 }