boot2

Playing with the boostrap
git clone https://git.ryansepassi.com/git/boot2.git
Log | Files | Refs | README

commit 1307fac52dc44f5fb029f5d56bfa657b9c7e1bcc
parent 1ecf0ceacd8cfc6c1cdf0407fa3d4e75408a66bf
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu, 30 Apr 2026 06:53:44 -0700

tcc todo update

Diffstat:
Mdocs/TCC-TODO.md | 524+++++++++++++++++++++++++++----------------------------------------------------
1 file changed, 181 insertions(+), 343 deletions(-)

diff --git a/docs/TCC-TODO.md b/docs/TCC-TODO.md @@ -1,389 +1,227 @@ -# tcc.flat.c via scheme cc — known blockers +# tcc-boot2 Current TODO -Working punch list for what stands between today's scheme1-hosted cc -and a successful compile of `tcc.flat.c`. Companion to -[TCC.md](TCC.md) (the surrounding three-stage pipeline) and -[CC.md](CC.md) (the C subset the cc accepts). +Current tracker for the scheme1-hosted `cc.scm` path that builds +`tcc.flat.c` into `tcc-boot2`. Historical parser, scratch, linker, and +runtime bring-up notes have been removed from this file; those fixes +are now covered by tests and by the build rules themselves. -## Repro +Companion docs: -`tcc.flat.c` is the stage-1 artifact. Generate it with the host -preprocessor: +- [TCC.md](TCC.md) describes the surrounding tcc pipeline. +- [CC.md](CC.md) describes the C subset and validation milestones. +- [LIBC.md](LIBC.md) describes the libc side used to link `tcc-boot2`. -``` -sh scripts/stage1-flatten.sh --arch X86_64 -# -> build/tcc/X86_64/tcc.flat.c (608 KB, 18 896 lines, 0 directives) -``` +## Current State + +`cc.scm` can compile the flattened tcc translation unit, the P1pp +output assembles and links, and `tcc-boot2` starts. The old blockers +around whole-file parse coverage, scratch exhaustion, tentative +definitions, anonymous members, `offsetof` const-expr, large AArch64 +stack frames, argv preservation, and `for`-`continue` lowering are +done and have focused regression tests. -Run the catm'd scheme cc against it inside the per-arch container. -The cc-debug flag prints heap usage between phases on stderr: +Useful smoke checks: +```sh +make tcc-boot2 ARCH=aarch64 + +build/aarch64/tcc-boot2/tcc-boot2 -v +build/aarch64/tcc-boot2/tcc-boot2 -E smoke.c +build/aarch64/tcc-boot2/tcc-boot2 -c smoke.c -o smoke.o ``` -podman run --rm --pull=never --platform linux/arm64 \ - --tmpfs /tmp:size=512M -e ARCH=aarch64 \ - -v "$(pwd)":/work -w /work boot2-busybox:aarch64 \ - build/aarch64/scheme1/scheme1 build/aarch64/cc/cc.scm --cc-debug \ - build/tcc/X86_64/tcc.flat.c /tmp/tcc.flat.P1pp + +For native generated-program testing, use the ARM64-targeted build via +the `tcc-cc` suite: + +```sh +make test SUITE=tcc-cc ``` -Prerequisites: `make scheme1 cc ARCH=aarch64` (or any other arch) so -`build/$ARCH/scheme1` and `build/$ARCH/cc/cc.scm` exist. +## `tcc-cc` Suite -For triage, the small-prefix probe is useful: +`tcc-cc` is the next acceptance suite. It runs the plain `tests/cc` +fixtures through `tcc-boot2` instead of through `cc.scm` directly. +The Makefile builds an ARM64-targeted `tcc-boot2`, builds the tiny +aarch64 `_start` object with the host assembler, then the runner does: -``` -head -c 50000 build/tcc/X86_64/tcc.flat.c \ - > build/tcc/X86_64/tcc.head.c -# then re-run the podman invocation against tcc.head.c +```sh +build/aarch64/tcc-boot2/tcc-boot2 \ + -nostdlib build/aarch64/tcc-cc/start.o tests/cc/NAME.c \ + -o build/aarch64/tests/tcc-cc/NAME + +./build/aarch64/tests/tcc-cc/NAME ``` -## Status — tcc-boot2 builds and runs +The result is compared against the same `.expected` and +`.expected-exit` files used by the regular `cc` suite. The suite is +aarch64-only today because it needs generated binaries to run natively +inside the aarch64 container. -The full 608 KB TU now parses to EOF (line 18800) and cg-finish emits -~6.5 MB of P1pp. No semantic-coverage gap remains in this TU. Last -aarch64 cc-debug run: +Run a subset with `NAMES`: -``` -[cc] phase=start: heap 1 225 052 -[cc] phase=slurp: heap 3 101 100 src-bytes 608 547 -[cc] decl: line 14861 heap 61 008 644 -[cc] decl: line 18024 heap 66 002 084 -[cc] decl: line 18800 heap 64 824 540 ; final decl -[cc] phase=parse: heap 64 864 516 -[cc] phase=cg-finish: heap 90 674 020 out-bytes 6 489 215 +```sh +NAMES='002-arith 007-call-with-args' make test SUITE=tcc-cc ``` -The emitted P1pp now assembles through m1pp → M0 → hex2 and links with -the mes-libc subset via the `tcc-boot2` make target. Runtime smoke -tests now pass under the aarch64 container: +## Latest `tcc-cc` Result +Fresh run: + +```sh +make test SUITE=tcc-cc ``` -build/aarch64/tcc-boot2/tcc-boot2 -v -# tcc version 0.9.26 (x86_64 Linux) -build/aarch64/tcc-boot2/tcc-boot2 -E smoke.c -# preprocesses successfully +Result: -build/aarch64/tcc-boot2/tcc-boot2 -c smoke.c -o smoke.o -# writes an x86-64 relocatable object +```text +13 passed, 140 failed ``` -The old traced aarch64 crash tail with `CC_TRACE_EMIT=1` was: +Raw run log: -``` -[trace @663108 cc__next_nomacro] -[trace @662d68 cc__next_nomacro_spc] -[trace @658d20 cc__next_nomacro1] -[trace @630580 cc__tok_alloc_new] -[trace @62d228 cc__tal_realloc_impl] -[trace @607bb4 memcpy] -[trace @6078e8 _memcpy] -Segmentation fault (core dumped) +```text +build/aarch64/.work/tests/tcc-cc/full-run.log ``` -Address lookup for the tail: +Passing fixtures: -``` -0x630580 cc__tok_alloc_new+0x30 -0x62d228 cc__tal_realloc_impl+0x30 -0x607bb4 memcpy+0x30 -0x6078e8 _memcpy+0x30 +```text +000-empty-main +000-return-argc +001-return-argc +002-add-const +003-local-assign +004-if-else +005-while-break +012-comparison +014-mul-paren +018-sext-narrow +026-sizeof-expr +049-init-scalar-global +072-enum-const ``` -That trace was misleading: temporary probes showed `tok_alloc_new` -completed and returned. Disassembly showed the real fault was a -truncated AArch64 stack-frame immediate. `cc__next_nomacro1` requested -a frame larger than 4095 bytes, but `aa64_sub_imm` masked the value to -12 bits, so later stack slots addressed memory outside the allocated -frame. `P1/P1-aarch64.M1pp` now emits one or two ADD/SUB-immediate -instructions for large immediates, including `%enter(size)` frames. -Regression: `tests/p1/large-addi.P1pp`. - -One follow-on runtime issue was also fixed: `P1/entry-libc.P1pp` now -saves `argc`/`argv` across `__libc_init`, so TCC actually receives its -command-line arguments. That exposed a compiler bug where `continue` -inside `for (...; ...; step)` jumped to the condition and skipped the -step expression. `cc/cc.scm` now lowers `for` loops so `continue` -lands on the step block. Regression: `tests/cc/133-for-continue.c`. - -Historical source review put the final `memcpy` after -`tal_realloc_impl` returns in `tok_alloc_new`: +Failure groups from per-fixture `tcc.log` files: -``` -ts = tal_realloc_impl(&toksym_alloc, 0, sizeof(TokenSym) + len); -... -memcpy(ts->str, str, len); -``` +| group | count | examples | +|------:|------:|----------| +| plain segfault during compile/link | 58 | `006-call-no-args`, `008-pointer-deref`, `011-struct`, `125-anon-union` | +| `store(...); assert fail: 0`, then segfault | 44 | `002-arith`, `004-inc-dec`, `020-switch`, `133-for-continue` | +| `assert fail: vtop[-1].r < VT_CONST && vtop[0].r < VT_CONST`, then segfault | 28 | `007-call-with-args`, `013-call`, `024-globals`, `132-tentative-bss-sizing` | +| compile succeeds, generated program exits wrong | 3 | `019-zext-narrow`, `068-main-noret`, `101-char-escapes` | +| `too many field init` diagnostic | 3 | `001-kitchen-sink`, `012-struct-ptr`, `053-init-struct-pos` | +| `__builtin_va_start` warning, then segfault | 3 | `015-variadic`, `076-vararg-recv`, `079-vararg-deep` | +| `field expected` diagnostic | 1 | `054-init-struct-desig` | -Harness target: `make tcc-boot2 ARCH=aarch64` (see Makefile + -`scripts/boot-build-cc.sh`) drives stage1-flatten on the host, runs -cc.scm on the flattened TU inside the container, and feeds the P1pp -into the standard `boot-build-p1pp.sh` pipeline. `TCC_TARGET` selects -which tcc codegen target gets baked into the binary -(default `X86_64`; use `ARM64` for aarch64); pick `ARCH` to match if -you want generated programs to run natively in the per-arch container. - -## Resolved — offsetof-style const expr in `options_W[]` (line 18026) - -Done. `parse-const-cast` now accepts pointer-typed casts as a type -re-tag (the integer offset rides through unchanged), and -`parse-const-unary` has an `&` arm that runs a small postfix-style -designator parser: a `(T *)0` head (with optional grouping parens or -a `*` deref) followed by a chain of `->` / `.` field selectors. Field -lookup reuses `%cg-find-field`, so anonymous union/struct members -(needed by `struct Sym`-style layouts) work without extra plumbing. -Scope is intentionally narrow — only the offsetof shape is admitted; -no general pointer arithmetic in const-expr. -Test: `tests/cc/126-offsetof-const.c` — covers `&((T*)0)->FIELD`, -`&(*(T*)0).FIELD`, and the same form through anonymous union members. - -## Resolved — scratch pressure on `asm_instrs[]` (line 14527) - -Done. `parse-init-global` still returns a pieces list to -`cg-emit-global`, but `%parse-init-array-list` now parses each array -element as a unit, promotes that unit's pieces plus parser/pp/lex -lookahead into main storage, rewinds scratch to a pre-unit mark, and -continues with the outer accumulator in main. This is selected by -initializer shape (file-scope/static positional aggregate), not by the -symbol name. - -Before the fix, parse reached the `static const ASMInstr asm_instrs[] = -{ ... };` table at line 14527 (~333 entries spanning lines -14528-14860) and aborted with `scheme1: scratch exhausted` partway -through. Per-element scratch growth measured then: +The important shape is that 137 of 140 failures happen before the +generated fixture binary runs. The dominant problem is still the +compiled `tcc-boot2` while it is compiling/linking C input, not the +runtime behavior of most generated test binaries. -``` -elem 16 -> scratch 287 503 672 -elem 176 -> scratch 400 656 488 -delta 113 152 816 over 160 elements ~= 707 KB / elem -``` +Working hypothesis: our compiler is miscompiling tcc itself. In this +suite, `tcc-boot2` is a tcc binary produced by `cc.scm`; the failures +look like that produced tcc is executing bad compiler/codegen logic and +therefore emitting bad code, asserting, or crashing while compiling the +fixtures. The host baseline below rules out the fixtures and expected +files as the main source of the failures. -Each row writes ~12 bytes of static data but consumed ~700 KB of -scratch to do it. tcc.c entries are dense: - -```c -{ TOK_ASM_cmpsb, - ((uint64_t) ((((0xa6) & 0xff00) == 0x0f00) - ? ((((0xa6) >> 8) & ~0xff) | ((0xa6) & 0xff)) - : (0xa6))), - (((0x01 | 0x1000)) | ((0) << 13) - | ((((0xa6) & 0xff00) == 0x0f00) ? 0x100 : 0)), - 0, { 0 } }, +A stronger control is to compile the same ARM64 `tcc.flat.c` with +Alpine gcc and use that gcc-built tcc to run the same `tests/cc` +fixtures. That control is not perfectly green, but it is far healthier: + +```text +gcc-built ARM64 tcc.flat.c: 126 passed, 27 failed +cc.scm-built ARM64 tcc-boot2: 13 passed, 140 failed ``` -Current aarch64 run: +So there are two layers of signal: -``` -[cc] decl: line 14527 heap 50929348 -[cc] decl: line 14861 heap 61008644 -delta 10079296 over 333 rows ~= 30 KB / row +- 27 failures reproduce even when tcc is built by gcc from the + flattened ARM64 source. Treat these as tcc/ARM64/flattened-source + baseline failures until proven otherwise. +- 114 additional fixtures fail only with the `cc.scm`-built tcc. That + is the main evidence that our compiler is miscompiling tcc. + +## Host Baseline + +The `tests/cc` fixtures themselves are coherent under a host compiler. +A temporary host harness was used to compile, run, and compare every +fixture with plain host `cc`: + +```sh +build/aarch64/.work/tests/tcc-cc/run-host-cc.sh ``` -The table now completes and parse advances to line 18026. The remaining -row-scale heap growth is persistent `.data` output plus promoted -pieces/metadata; it is no longer a scratch cap blocker. - -## Resolved — `static` tentative-def merge - -Done. handle-decl now records file-scope `int x;` / `static int x;` -(no init, non-extern) as tentative defs (`defined?=#f`) and adds the -name to a new `world-tentatives` list rather than emitting BSS at decl -time. `cg-finish` walks the list and emits `.bss` for any tentative -that didn't get a real definition, so two `static int gnu_ext;` -followed by `static int gnu_ext = 1;` merges cleanly via the existing -`sym-merge` (defined? wins). Test: `tests/cc/124-tentative-static.c`. - -## Resolved — anonymous union/struct members (`s->d`, `s->c`) - -Done. tcc.c's `struct Sym` uses three back-to-back anonymous unions -(`union { long c; int *d; }` etc.) and accesses them as if they were -direct members. `%cg-find-field` and `%find-field` now recurse into -nameless struct/union members, returning a synthetic -`(name ctype composed-offset)` triple. `%parse-init-local-struct-list`'s -zero-pass also got an anon-aware `%anon-touched?` helper so a -designator like `.a = 10` on a struct with anon-union members no -longer gets clobbered by the trailing zero-fill. -Test: `tests/cc/125-anon-union.c`. - -## Resolved — `sizeof EXPR` in const-expr context - -Done. `char buf1[sizeof file->filename];` (line 3867) and similar. -The existing `parse-unary` sizeof handler already used cg -snapshot/rewind to recover the operand's ctype without evaluating it; -const-expr now does the same via `%const-sizeof-expr`. Both -parens-form and bare-`sizeof EXPR` work. - -## Resolved — FP softening (cast-to dbl at line 4205) - -Done. `parse_number` declares `double d; d = 0;` which triggered the -`cg-cast` FP rejection. `%cg-fp-reject!` is now a named no-op so -fp ctypes flow through size-dispatched load/store and same-size casts -as raw bit patterns. Real FP arithmetic is still wrong (binops emit -integer ALU ops on the underlying bits), but tcc-boot2's runtime -never executes its own float code paths when compiling float-free -programs, so producing valid-but-semantically-wrong P1pp here is -sufficient. Comment in cc.scm flags the call sites for any future -target that needs real FP. - -## Resolved — `__attribute__` decl-spec at line 1628 - -The cc now consumes GNU `__attribute__ ((...))` specs and discards -them. Skip lives next to `eat-cv-quals!` in cc.scm; called from -`parse-decl-spec` (prefix attributes) and `parse-decl-suf-cont` -(trailing attributes after a declarator, e.g. -`void foo(void) __attribute__((noreturn));`). Same softening pattern -as floats / wide types — `noreturn`, `format`, `aligned`, etc. are -not honoured semantically, just parsed away. - -## Resolved — parse-phase heap pressure - -The two earlier memory blockers (whole-TU heap explosion, single-decl -scratch peak inside the `enum tcc_token` block) are both gone after -the per-decl scratch arena (Phase 3) plus the static aggregate -initializer unit rewind path ([CC-INIT-SCRATCH.md](CC-INIT-SCRATCH.md)) -plus the recent scope-bind alist / scratch reclamation work. - -Current full-file aarch64 run against -`build/tcc/X86_64/tcc.flat.c` — parse + cg-finish complete: +Current host baseline: +```text +HOST_CC=cc +HOST_CFLAGS=-std=gnu11 -w +153 passed, 0 failed ``` -[cc] phase=start: heap 1 225 052 -[cc] phase=slurp: heap 3 101 100 src-bytes 608 547 -[cc] decl: line 14527 heap 50 929 348 -[cc] decl: line 14861 heap 61 008 644 -[cc] decl: line 18024 heap 66 002 084 -[cc] decl: line 18800 heap 64 824 540 ; final decl -[cc] phase=parse: heap 64 864 516 -[cc] phase=cg-finish: heap 90 674 020 out-bytes 6 489 215 + +The gcc-built flattened-tcc control runs in the Alpine gcc image: + +```sh +podman run --rm --pull=never --platform linux/arm64 \ + -v "$PWD":/work -w /work boot2-alpine-gcc:aarch64 \ + sh build/aarch64/.work/tests/tcc-cc/run-gcc-flat-tcc.sh ``` -Milestones from that run: - -| point | line | heap | Δ from start | note | -|------:|-----:|-----:|-------------:|------| -| start | - | 1 225 052 | - | runtime before slurp | -| slurp | - | 3 101 100 | 1 876 048 | 608 547-byte source loaded | -| before `asm_instrs[]` | 14 527 | 50 929 348 | 49 704 296 | enters large static table | -| after `asm_instrs[]` | 14 861 | 61 008 644 | 59 783 592 | table completed | -| through options tables | 18 024 | 66 002 084 | 64 777 032 | offsetof const-expr region | -| end of TU | 18 800 | 64 824 540 | 63 599 488 | final decl reached | -| post cg-finish | - | 90 674 020 | 89 448 968 | text/data buffers + 6.5 MB out | - -Observed rates: - -- Parse to EOF holds steady around ~64.8 MB above start for 608 547 - source bytes — i.e. ~110 bytes of resident state per source byte. -- The `asm_instrs[]` table adds ~10.1 MB over 333 rows, about - 30 KB / row after the streaming initializer fix. -- cg-finish adds ~26 MB on top of parse and produces 6.5 MB of P1pp. - -Older prefix probes that end at clean top-level `};` boundaries -(HEAP_CAP_BYTES = 256 MiB, SCRATCH_CAP_BYTES = 128 MiB): - -| line | bytes | heap after parse | Δ from start | KB / source byte | -|-----:|-------:|-----------------:|-------------:|-----------------:| -| 220 | 7 953 | 18 012 476 | 16 802 432 | 2.11 | -| 280 | 9 795 | 18 885 492 | 17 675 448 | 1.81 | -| 683 | 18 260 | 21 464 308 | 20 254 264 | 1.11 | -| 880 | 22 111 | 22 650 284 | 21 440 240 | 0.97 | -| 981 | 24 557 | 23 281 212 | 22 071 168 | 0.90 | -| 986 | 24 630 | 23 306 676 | 22 096 632 | 0.90 | -| 1612 | 40 943 | 31 186 500 | 29 976 456 | 0.73 | -| 1627 | 41 626 | 31 481 060 | 30 271 016 | 0.73 | - -Marginal residency converges to roughly **0.9 KB / input byte** at -the small-prefix scale and drops further across the enum block. The -current full-file run confirms the 608 KB TU fits comfortably under -the existing heap and scratch caps until the line 18026 const-expr -coverage failure. - -The full 608 KB TU itself slurps to a heap of 3 101 100 bytes (~3 MB) -— bytevector storage for the source plus runtime baseline. - -Heap delta minus the start-of-process baseline (~1.21 MB scheme1 -runtime + cc-init bufs at ~12 MB): +Current result: +```text +tcc version 0.9.26 (AArch64 Linux) +126 passed, 27 failed ``` -parse_heap - start_heap -= persistent main-heap state introduced by parse -= surviving roots (scope, tags, str-pool) + cg-text/data/bss bytes + +Those 27 failures break down as: + +- Compile/link segfaults: `001-kitchen-sink`, `015-variadic`, + `032-local-struct-desig`, `067-vararg-call`, `076-vararg-recv`, + `079-vararg-deep`, `084-struct-assign`, `096-fwd-struct`, + `097-vararg-many-named`, `099-init-zero-tail`, + `108-typedef-fnptr`, `109-typedef-anon`, `111-struct-ret-1word`, + `112-struct-ret-2word`, `113-struct-ret-3word`, + `114-struct-ret-many-args`, `115-struct-ret-3word-many-args`, + `116-struct-ret-vararg`, `117-compound-literal`, + `125-anon-union`, `129-extern-libp1pp`, `131-vararg-mixed`. +- Runtime exit mismatches: `018-sext-narrow`, `068-main-noret`, + `102-cmpd-narrow`, `119-float-parse`, `128-cast-signedness`. + +Two fixture cleanups are part of that baseline: + +- `tests/cc/125-anon-union.c` explicitly initializes its local struct + before probing anonymous-union aliasing. Tests should not depend on + implicit zeroing of automatic locals. +- `tests/cc/132-tentative-bss-sizing.c` returns distinct numeric exit + codes instead of calling `sys_write`/`strlen`. Plain `tests/cc` + fixtures should not need stdio/libc helpers. + +The cleaned fixtures also pass the regular aarch64 `cc` path: + +```sh +NAMES='125-anon-union 132-tentative-bss-sizing' \ + make test SUITE=cc ARCH=aarch64 +# 2 passed, 0 failed ``` -The cg-init bufs themselves account for ~12 MB of the start baseline -(see %BUF-CAP-* in cc.scm). After amortizing them out, persistent -parse state at the 1k-line scale is closer to **0.4 KB / input -byte**. - -### History — what the earlier numbers looked like - -For posterity. Pre-Phase-3, parse-phase residency was roughly -6.5 KB heap per source byte (1612-line cut: ~267 MB, just under cap; -50 000 B head and full TU: heap exhausted). Post-Phase-3 dropped that -to roughly 0.9 KB / byte at the steady state but left a single-decl -*scratch* peak: the `enum tcc_token` block (lines 987–1612, 800+ -enum constants) overflowed even 128 MiB of scratch because -`scope-bind!`'s `alist-ref` walk made cumulative per-decl scratch -O(N²) in member count. The recent scratch / alist work makes that -decl complete with parse heap at ~31 MB on the 1612-line cut. - -## Tracepoint instrumentation (`%trace` / `--cc-trace-emit`) - -See [DEBUG.md](DEBUG.md) — `CC_TRACE_EMIT=1` injects per-function-entry -stderr probes; `m1-symbols.py lookup` resolves the printed addresses -back to functions. `%trace` now saves/restores all exposed P1 registers -(`a0..a3`, `t0..t2`, `s0..s3`) by borrowing stack space inside the -current `%fn` frame, so manual probes can be inserted in live code -without clobbering caller state. - -## Expected next-tier blockers (downstream of cc.scm) - -The semantic parser has covered every construct in this TU, and the -large P1pp output now makes it through m1pp / M0 / hex2. The next likely -walls are runtime/codegen mismatches: - -- **`tcc-boot2 -version` correctness**. Even when the toolchain - produces an ELF, the runtime still has to walk through tcc's setup - (string-table init, command-line parsing, output for `-version`) - without tripping on cg semantics that pass the small tests but - diverge from C in subtle ways. -- **Struct layout / flexible-tail object correctness**. The current - crash path is `tok_alloc_new` copying into `TokenSym::str`, so offsets - around `TokenSym`, `TinyAlloc`, and related tcc structs are high-value - targets for small focused tests. -- **libc behavior under full tcc load**. The mes-libc subset is now in - the link, but runtime helpers still need validation under tcc's actual - allocation/string/token workloads. - -The end goal is milestone 4 in [CC.md §Validation milestones](CC.md) -— "Compile tcc.c (under the tcc-mes defines) → tcc-boot2; verify -`tcc-boot2 -version` runs." - -## libc — see [LIBC.md](LIBC.md) - -The unresolved externals in [LIBC.txt](LIBC.txt) are met by porting a -curated subset of mes libc and adding a thin syscall-wrapper file that -calls P1pp's `sys_*` labels directly. **musl is rejected** for this -layer (built around gcc-only idioms — inline asm everywhere, TLS -`errno`, weak/visibility attrs, `_Atomic`, IEEE math, dynamic linker — -none of which survive cc.scm's subset). - -The work splits cleanly into two phases. **Phase A** is what makes -tcc-boot2 itself link: cc.scm compiles the vendored mes libc subset -into `libc.P1pp`, catm'd with `tcc.P1pp` to produce the tcc-boot2 -ELF. **Phase B** is what makes tcc-boot2 *useful* — tcc-boot2 -auto-appends `-lc` and resolves runtime helpers -(`__divdi3`, `__floatundidf`, …) against `$LIBDIR/tcc/libtcc1.a` when -linking the code it compiles. Both archives have to exist on disk or -even hello-world won't link. Phase B uses tcc-boot2 itself as the -compiler (mirrors live-bootstrap's `pass1.kaem` substituting tcc-boot2 -for tcc-mes); upstream tcc's `lib/libtcc1.c` is the source for the -libtcc1.a side and is already pulled in by `stage1-flatten.sh`'s -tarball unpack. - -The full implementation handoff (manifest of files to vendor, the four -surgical patches, the new P1pp entry points, the Phase A / B build -scripts, smoke tests, acceptance criteria) lives in -[LIBC.md](LIBC.md). - -CC.md needs a follow-up edit: its "we link against the same `libc+tcc` -archive MesCC uses" line is now stale. +## Next Debug Targets + +Start with the earliest minimal failures in each dominant group: + +- `002-arith`: first `store(...); assert fail: 0` case. This is a + small arithmetic fixture and is likely the highest-leverage entry + point into the ARM64 tcc codegen path compiled by `cc.scm`. +- `007-call-with-args`: first clear `vtop[-1].r < VT_CONST` assertion + on an ordinary call with arguments. +- `006-call-no-args`: first plain segfault with a very small source. +- `001-kitchen-sink` or `012-struct-ptr`: first incorrect tcc parser + diagnostics around aggregate initialization. +- `019-zext-narrow`, `068-main-noret`, `101-char-escapes`: the only + current failures where `tcc-boot2` successfully emits and links a + binary but the binary returns the wrong status. + +Keep using `make test SUITE=cc ARCH=aarch64 NAMES=...` as the control +path for fixture semantics, and `make test SUITE=tcc-cc NAMES=...` as +the `tcc-boot2` acceptance path.