feat(kernel): R7a C/C++ walker — dual-lang ccpp module, preParse hoist, 7 new blanks, c/cpp default-routed (#1346)

Parity: 0 diffs on redis/git/fmt/protobuf/ALS sweeps; full-init dumps
byte-identical on all five + linux at kernel scale (10.4M dump lines,
same sha256 both arms). Linux 2c/6GB envelope: kernel-arm 19.1min vs
wasm-arm 22.9min (parse 356s vs 435s) on a much richer graph (the new
blanks recover error-swallowed code: git 2x nodes, linux kernel/+mm/ 3x).
Deferral guard corrected by measurement (C/C++ error incidence 9-42%;
--max-deferral flag); defer-reuse memo kills the 3x re-blank/re-parse
cost deferred files paid.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Colby Mchenry
2026-07-17 16:56:41 -05:00
committed by GitHub
co-authored by Claude Fable 5
parent 44561b6aad
commit 2d72891b59
20 changed files with 3211 additions and 80 deletions
+65 -12
View File
@@ -1,17 +1,70 @@
# C/C++ kernel port (R7a) — the bug-for-bug checklist
**Status:** survey COMPLETE; grammars VENDORED + suite-green (2026-07-17):
tree-sitter-c v0.24.2 (`b780e47`, parser.c `f2883ff9…`) + tree-sitter-cpp
v0.23.4 (`f41e1a0`, parser.c `2a35a43b…`, scanner.c `cf60387d…`), built with
ts-cli 0.25.10 from checked-in parser.c, in `src/extraction/wasm/` +
VENDORED_WASM_LANGS. The walker PR adds the SAME-version crates + kernel
grammar registry (kernel-grammar-parity then pins the alignment). Walker +
gates not started.
This is §0a-recipe step 1's output for c/cpp: every TS-side branch the walker
must mirror, with file:line anchors into the reference implementation. Read it
WITH `docs/design/rust-kernel-migration-plan.md` (§0a recipe, §5 gates).
Companion walkers to crib structure from: `codegraph-kernel/src/tsjs/` (multi-
dialect module), `java.rs`, `go.rs` (receiver QNs), `python.rs`.
**Status: COMPLETE — walker SHIPPED + gates PASSED, c/cpp DEFAULT-ROUTED
(2026-07-17).** Walker: `codegraph-kernel/src/ccpp/mod.rs` (one dual-language
module, every branch below mirrored; its header comment lists the quirks).
Grammars: tree-sitter-c v0.24.2 (`b780e47`, parser.c `f2883ff9…`) +
tree-sitter-cpp v0.23.4 (`f41e1a0`, parser.c `2a35a43b…`, scanner.c
`cf60387d…`), crates pinned `=exact` in Cargo.toml, wasm vendored from the
same tags, kernel-grammar-parity green. preParse HOISTED to the route point
(`preParsedSource` in src/extraction/kernel/index.ts — both tryKernelExtract
and the raw bulk path), so no blanking ported to Rust.
**Gate results (2026-07-17):**
- Parity sweeps — **0 diffs on every compared file**: redis 592, git 790,
fmt 42, protobuf 925, ALS-Community (UE spot-check) 40 files byte-parity.
- Full-init dump-diffs — **byte-identical** kernel-arm vs wasm-arm: redis
(131,921 dump lines), git (160,844), fmt (35,433), protobuf (780,620),
ALS (3,694).
- Torture fixtures torture.c/.cpp/.hpp + CRLF variants + Metal/CUDA
hoist-parity + defer tests in `__tests__/kernel-ccpp-parity.test.ts`; new
preParse blanks unit-tested in extraction.test.ts; full suite green with
`CODEGRAPH_KERNEL_EXPECT=1`.
- **Deferral-rate guard — CORRECTED BY MEASUREMENT** (the §4f pattern): the
<10% bar was calibrated on ts/java/py/go (00.42% parse-error incidence).
Macro-heavy C/C++ genuinely parses with errors at double-digit file rates
(final sweeps: als 9%, git 16.1%, redis 25.3%, protobuf 25.8%, fmt 42% —
fmt's template metaprogramming + `.operator[]`-in-decltype shapes are
grammar-inherent), and every erroring file defers BY POLICY. Measured with
the defer disabled (`CODEGRAPH_KERNEL_CCPP_ERROR_EXTRACT=1`, sweep-only
hatch): recovery-divergence is real (21/207 redis, 8/382 git, 9/31 fmt
erroring files extract differently across UTF-8/UTF-16), so the defer
stays. The sweep harness now takes `--max-deferral` (default 0.1; use 0.5
for c/cpp — a broken walker still trips it by deferring ~everything).
- **Seven NEW/extended preParse blanks** cut real incidence (from 32%/52%
starting points; linux `kernel/`+`mm/` subtrees 79% → 58%), each
offset-preserving, TS-side, shared by both arms, and a graph-quality win
for the wasm path itself (git 7.1k → 13.3k nodes; the linux subtrees
2.4k → 7.2k): `#ifdef __cplusplus` guard bodies, lone macro lines
(`FMT_BEGIN_NAMESPACE`, `Q_OBJECT`), C statement iterator macros
(`list_for_each_entry(…) { }` and brace-less bodies), C trailing param
attrs (`int argc UNUSED`), the curated Linux/sparse annotation list
(`__init`/`__user`/… — structural matching is impossible there: `__u32
count` is shape-identical; parameterized `__printf(1,2)` guarded out) +
`container_of`'s type-keyword argument, leading attr macros extended to
cpp, and directive-line restore (stops the older blanks corrupting
`#define` lines).
- **Defer-reuse (the linux-economics fix):** a deferred file used to pay the
pipeline three times — the worker's raw kernel try, extractFromSource's
kernel RE-try, then wasm with a third preParse. A one-slot defer memo in
the route point short-circuits the repeat kernel attempt and hands the
already-blanked source to the wasm fallback (`sourceIsPreParsed`). On
linux this + the annotation blanks took the kernel-arm parse-loop from
560s (WORSE than the 426s wasm arm) to **356s vs 435s wasm-arm (18%)**,
and the 2c/6GB envelope to **19.1 min kernel-arm vs 22.9 min wasm-arm
(17%)** with a RICHER graph (2,048,295 nodes / 6,406,933 edges; two
independent kernel-arm runs byte-same counts).
- **Linux-scale dump gate:** kernel-arm and wasm-arm full graphs are
**byte-identical**`dump-graph.mjs` over both 5.2GB DBs:
10,444,551 dump lines each, sha256 `cd4182e6…` on both. (Dumping a 2M-node
DB needs a big-heap host run — `node --max-old-space-size=16000 …
> file` then hash the FILE; the in-container 6GB heap OOMs and a straight
pipe at GB scale dies with ENOBUFS, both of which silently hash truncated
output as the empty stream.)
The sections below are §0a-recipe step 1's output — every TS-side branch the
walker mirrors, with file:line anchors (as of `705e501`). Read WITH
`docs/design/rust-kernel-migration-plan.md` (§0a recipe, §5 gates).
## Architecture decisions (already made by the plan)
+48 -26
View File
@@ -62,26 +62,44 @@ them are the ORIGINAL plan and carry expectations that measurement later correct
Record runs DONE (§7a.2): 2c/6GB 20.4min, 8c/7GB 18.3min NO-OOM — byte-exact.
Batch-loop profile round DONE (§7a.3, #1339): countGuard quadratic killed,
19.3min. cFnPtr round DONE (§7a.4, #1341): 2.07× standalone, edge set
hash-identical, envelope **17.6min (R6 33%)**. The <10min-on-8c target
remains open; levers left, ranked: **R7a C/C++ port (parse 338s — the
last big rock)** > backpressure ~120s (checkpoint I/O floor) > E-scan/
settle/read-mapping (~7090s each, approaching honest work) > the 8c
re-run formality (est. ~15.5min).
- [ ] **R7a. C/C++ port** — STARTED 2026-07-17: the §0a-recipe step-1 survey is
COMPLETE — every TS branch, quirk, and helper is enumerated with line
anchors in **`docs/design/ccpp-kernel-port-checklist.md`** (read it FIRST;
it also fixes the architecture: preParse hoisted to the route point so no
blanking ports to Rust, Metal/CUDA stay wasm this round, one dual-lang
walker module). Next: grammars (upgrade+vendor from matched tags, suite
green BEFORE the walker), then the walker, then the §5 gate ladder.
Original scope note: biggest single-language effort; unlocks cg1212's parse
expectation (6.2m → ~1.52m, 23% of that wall) + CARLA/UE/llvm-class repos;
Metal + CUDA ride along (their blanking pre-passes stay TS-side — `preParse`
is offset-preserving and the route point can apply it before the kernel call;
see the T2 note in `src/extraction/kernel/index.ts`). Largest per-language
surface in tree-sitter.ts: namespace prefix stacks (#1291), local fn-pointer
tables (#932), operator calls (#1247), stack construction (#1035), macro
salvage + `.h` content detection (stays at detectLanguage, upstream — free).
hash-identical, envelope **17.6min (R6 33%)**. R7a landed 2026-07-17:
envelope now **19.1min on a substantially RICHER graph** (the new
preParse blanks recover previously-error-swallowed code; wasm-arm on
the same graph is 22.9min — the 17.6 record was the old smaller graph
and isn't directly comparable). The <10min-on-8c target remains open;
levers left, ranked: **C/C++ deferral cuts (58% of linux files still
defer to wasm — each recovered idiom moves parse toward the native
floor)** > backpressure ~120s (checkpoint I/O floor) > E-scan/settle/
read-mapping (~7090s each, approaching honest work) > the 8c re-run
formality.
- [x] **R7a. C/C++ port** — DONE 2026-07-17, same-day walker+gates after the
survey (#1344) and grammar vendoring (#1345). One dual-language walker
(`codegraph-kernel/src/ccpp/`), preParse HOISTED to the route point
(both tryKernelExtract and the raw bulk path — no blanking ported to
Rust; Metal/CUDA ride the cpp route through the same hoist). Gates:
parity sweeps **0 diffs** on redis/git/fmt/protobuf/ALS (2,389 files
compared); full-init dump-diffs **byte-identical** on all five;
DEFAULT_ROUTED += c, cpp. Three measurement corrections recorded in
the checklist doc: (1) C/C++ parse-error incidence is 942% per repo
(vs 00.42% for prior languages), so erroring-file deferral is
routine, not a broken-kernel signal — the sweep gained
`--max-deferral` (0.5 for c/cpp) after confirming recovery-divergence
is real with the sweep-only no-defer hatch; (2) seven new/extended
TS-side preParse blanks (extern-C guard bodies, lone macro lines,
statement iterator macros, trailing `UNUSED` params, the curated
Linux/sparse `__init`-family annotations + `container_of` type args,
cpp leading-attr, directive-line restore) cut real incidence (linux
subtrees 79% → 58%) AND grew the wasm path's own graphs (git
7.1k → 13.3k nodes) — so cg1212's "counts must stay
2,048,664/6,405,964" expectation is superseded: the graph legitimately
changes with the blanks; the invariant is kernel-arm == wasm-arm at
every scale (held: five byte-identical dumps + the linux dump-hash
pair); (3) at high deferral the kernel arm initially LOST arm-vs-arm
on linux (deferred files ran the pipeline 3×) — fixed with the
one-slot defer memo + blanked-source reuse; final cg1212 envelope
**19.1 min kernel-arm** (parse-loop 560 → 356s; R6 26.4 → P1 17.6 on
the old smaller graph → 19.1 on the new richer one:
2,048,295 nodes / 6,406,933 edges, two runs byte-same).
- [ ] **R7b. Remaining long tail** per the tracker (§4) — ruby/php/csharp/rust/… T1s
are now ~1-day-each with the walker pattern; T3 may stay TS forever (fine).
- [ ] **P2. Arc 3, graph richness** (§7b) — product-priority call, standard gates.
@@ -98,7 +116,8 @@ and has the current build deployed at `/app` (tree at `/work/linux`).
**What exists:**
- `codegraph-kernel/` — napi-rs crate. One WALKER MODULE per language
(`tsjs/`, `java.rs`, `python.rs`, `go.rs`) mirroring `TreeSitterExtractor`'s
(`tsjs/`, `java.rs`, `python.rs`, `go.rs`, `ccpp/` for c+cpp) mirroring
`TreeSitterExtractor`'s
per-language paths bug-for-bug; shared `buffers.rs` (wire contract — twin of
`src/extraction/kernel/layout.ts`, byte-matched, ABI-versioned), `ids.rs`
(sha node ids, test-pinned to `generateNodeId`), `docstring.rs`, `textutil.rs`
@@ -106,10 +125,13 @@ and has the current build deployed at `/app` (tree at `/work/linux`).
(grammar registry).
- `src/extraction/kernel/` — loader (contract-verifies before routing; a stale
.node silently degrades to wasm; `CODEGRAPH_KERNEL_DEBUG=1` explains), decode,
routing (`DEFAULT_ROUTED` = ts/tsx/js/jsx/java/python/go;
`CODEGRAPH_KERNEL_LANGS` REPLACES the set; `CODEGRAPH_KERNEL=0` kills), and the
routing (`DEFAULT_ROUTED` = ts/tsx/js/jsx/java/python/go/c/cpp;
`CODEGRAPH_KERNEL_LANGS` REPLACES the set; `CODEGRAPH_KERNEL=0` kills), the
deferred-decode transport (`tryKernelExtractRaw` → buffers ride to the store
worker; files with applicable framework `extract()` hooks keep the decoded path).
worker; files with applicable framework `extract()` hooks keep the decoded
path), and the **preParse hoist** (`preParsedSource` — a language's
offset-preserving `preParse` hook runs before BOTH kernel entry points, so
c/cpp/metal/cuda blanking stays TS-side and both arms parse identical bytes).
- Gates in-repo: `scripts/kernel-parity.mjs` (per-file kernel↔wasm diff,
ORDER-sensitive, full-object; deferral-rate guard), `scripts/dump-graph.mjs`
(natural-key full-DB dump for the byte-identical diff),
@@ -490,8 +512,8 @@ parity before porting the language.
| rust, dart, scala, lua, luau, r | dedicated files | T1 | crates.io (luau/r/scala: verify crate freshness vs our wasm) | Long-tail T1; port opportunistically after the big five. | ☐ |
| kotlin | `languages/kotlin.ts` | T1½ | crates.io | Expect/actual pairing is synthesis-side (fine); extraction is clean but validate against a KMP repo. | ☐ |
| swift | shared + dedicated branch | T1½ | crates.io | **Trap:** in-class property extraction lives in `tree-sitter.ts`'s DEDICATED branch, not `swift.ts` (#1020 — Alamofire went 0→348 props). Gate on Alamofire. | ☐ |
| c, cpp | `languages/c-cpp.ts` | **T2** | crates.io | Keep as TS pre-passes: `blankCppExportMacros`/`blankCppInlineMacros` (UE `class MACRO Name` phantom-function misparse, #1096#1102, CARLA 440→6), in-body reflection collapse guard (#1206), content-based `.h` C-vs-C++ detection. | |
| metal, cuda | dialects over the cpp grammar | **T2** (rides c/cpp) | crates.io (cpp) | README-listed as first-class languages. Both are dialect-gated cpp: Metal = specifier/`[[attribute]]` blanking (#1121, the preParse-takes-filePath pattern); CUDA = `<<<>>>` blanking + content-gated `.h` (#1172). Their pre-passes must run before the kernel parse or stay TS-side; gate them WITH the c/cpp port, not separately. | |
| c, cpp | `languages/c-cpp.ts` | **T2** | crates.io | **DONE (R7a, 2026-07-17)** — `ccpp/` walker; ALL pre-passes stayed TS-side via the route-point preParse hoist (+6 new blanks added during gating — see the checklist doc); content-based `.h` C-vs-C++ detection stays upstream at detectLanguage. Parity 0-diff + dump byte-identical on redis/git/fmt/protobuf/ALS. | |
| metal, cuda | dialects over the cpp grammar | **T2** (rides c/cpp) | crates.io (cpp) | **DONE (rides R7a)** — `.metal`/`.cu`/`.cuh` map to 'cpp' and their blanks run in the hoisted preParse (filePath rides along for the extension gates); hoist-parity pinned in kernel-ccpp-parity.test.ts + the metal/cuda suites. | |
| objc | `languages/objc.ts` | T2 | crates.io | Rides the c-cpp trap family; RN bridge extraction feeds `rnCrossPlatformEdges` (synthesis-side, fine). | ☐ |
| arkts | `languages/arkts.ts` | T2 | **vendored** (harmony-contrib) | Dot-prefixed refs + decorator-gated matching fixed 36,840 wrong edges — that logic must port exactly or stay TS-side. Compile our grammar fork natively. | ☐ |
| pascal | `languages/pascal.ts` | T2 | **vendored** | Paired with dfm-extractor (T3); `extractPascalDefProc` indexed lookups. | ☐ |