P82 used chipcc, our toy compiler, to prove that Linux on the chip
could execute a generated file. P83 replaces that toy with a real
TinyCC-derived compiler: the sellicott/tcc-riscv32 fork.
The initramfs now includes /bin/tcc. BusyBox ash runs:
tcc -static -nostdlib -o /tmp/p83_tcc_hello /usr/share/p83/tcc_hello.c
chmod +x /tmp/p83_tcc_hello
/tmp/p83_tcc_hello
The generated program printed:
P83-TCC-COMPILED-ON-CHIP
What Worked
The pinned TCC fork built into a static RV32 Linux executable. In the
passing run /bin/tcc was 422,904 bytes. Running that compiler inside
the guest produced a 584-byte static RV32 executable in /tmp.
The source is intentionally minimal. It defines _start, uses inline
RISC-V assembly to make SYS_write and SYS_exit, and avoids libc
startup, headers, floating point, and normal main.
What Bit Us
GCC-style register variables were not enough for this TCC fork. The
first generated program stored the desired syscall register values on
the stack, reached ecall with stale registers, and spun forever. The
working source loads a0, a1, a2, and a7 directly in inline
assembly.
Verification
tty-smoke produced these markers:
P83-BUSYBOX-SMOKE
P83-UNAME-OK
P83-TOOLS-OK
P83-BACKSPACE-OK
P83-TCC-OK
P83-TCC-COMPILED-ON-CHIP
P83-COMPILE-OK
P83-FILE-OK
The final marker arrived after 209,262,630 post-load cycles with all 357 host input bytes delivered.
Honest Status
| check | status |
|---|---|
Static RV32 /bin/tcc from pinned fork | PASS |
| TCC runs inside the guest | PASS |
TCC emits /tmp/p83_tcc_hello on the chip | PASS |
| Generated RV32 executable runs on the chip | PASS |
Normal main + libc + headers | NOT RUN |
| Upstream TinyCC RV32 support | NOT RUN |
| LibreLane hardening | NOT RUN |
Profiling Next
This path is now real enough to profile. The next project should turn the existing PC samples, state counters, and milestone cycles into a flamegraph-style notebook page: where time is spent, what is Linux, what is shell/TCC/userspace, and which RTL/runtime features would buy the most speed.