Skip to content

Fuzix for Pico Computer 2/3 v0.3 - native code generation

Choose a tag to compare

@UKTailwind UKTailwind released this 31 Jul 12:19

The compiler now generates native ARM code — and its output runs as ./prog.

# cc hello.c
# ./hello.bc
hello from Fuzix

cc still reads the same C89-plus dialect and still validates against gcc byte-for-byte (165 of 175 applicable c-testsuite tests, unchanged), but what it emits is no longer interpreted bytecode alone: every function the translator covers is compiled to ARM Thumb-2 machine code and runs on the metal, with the bytecode retained as a per-function fallback. Coverage is complete enough that whole real programs go fully native.

Measured on the Pico Computer 3, same programs, before → after:

benchmark v0.2 (interpreted) v0.3 (native)
sieve of Eratosthenes 3.85 s 47 ms
fib(27) 2.64 s 71 ms
shellsort 3.66 s 74 ms
xorshift PRNG 8.18 s 57 ms
Dhrystone 2.1 3,900/s 16,961/s
solar eclipse (3,200-line BASIC → C) 9.19 s 3.47 s

For calibration, the same Dhrystone cross-compiled by arm-none-eabi-gcc as a native Fuzix binary runs at 379,000/s — the bytecode machine's compiler now stands within ~22× of a first-class optimizing compiler, from ~100× in v0.2. The eclipse figure beats MicroPython on the same silicon (8.77 s) and MMBasic (12.5 s) — a BASIC program machine-translated to C, compiled on this machine or off it, running faster than both interpreters.

Under the hood, in the order it happened: double-precision arithmetic moved to the RP2350's DCP coprocessor (benefits everything, BBC BASIC included); the code generator grew 64-bit and floating-point coverage, switch, struct copy, direct calls between native functions, and compare/branch fusion; the runtime binds every library call once instead of string-matching per call; and dead bytecode is reclaimed so full-native programs fit a 256 KiB process.

New: a PSRAM arena. A process can ask the kernel for a region of the 8 MiB PSRAM outside its own image — not counted against the process size, never copied on context switch, never swapped (PSRAMIOC_ALLOC/FREE/STAT on /dev/sys, 1 MiB pool by default, psram=<n>[K|M] at the bootdev prompt to retune). The compiler is its first client: cc2's tables live there, which is what lifted the on-board compiler to host-class capacity limits. Fork leaves an arena with the parent; exec and exit reclaim it.

New: #! execution. The kernel execs #!-marked files through /usr/bin/bcrun; cc marks and chmods its output, so compiled programs launch like any other binary. Older .bc files still run via bcrun prog.bc.

Also fixed along the way: a block-device bounds check that only covered the first sector of a transfer, warm-reset SD/flash/boot-state bugs that could brick a reset until reflash (three separate causes, all found and fixed), a latent truthiness bug on denormal comparison results, and %lld printing through a libc that reads long for ll.

Flash fuzix.uf2, write pc3-sd.img.gz (decompressed) to an SD card, and the samples in /root/cc are waiting. The manual is unchanged from v0.2; the new features are documented in the tree (PC3-PSRAM-ARENA.md, Applications/CC/PLAN-arm-backend.md).

🤖 Generated with Claude Code