MicroCC - A Self-Hosting C Compiler for Microcontrollers
MicroCC is a self-hosting C compiler that runs entirely on a Raspberry Pi Pico (RP2040). It compiles C code to ARM Thumb machine code and executes it via JIT — all on a $4 microcontroller with 264KB of RAM.
Quick Start
- Hold BOOTSEL button on your Raspberry Pi Pico
- Connect USB cable to your computer
- Release BOOTSEL — the Pico will appear as a USB drive (RPI-RP2)
- Drag and drop
microcc-repl.uf2to the drive - Connect serial terminal at 115200 baud:
# macOS screen /dev/tty.usbmodem* 115200 # Linux screen /dev/ttyACM0 115200 # Or use minicom minicom -D /dev/ttyACM0 -b 115200
Demo Session
Here's what you'll see when running the REPL on actual Pico hardware:
========================================
MicroCC REPL
========================================
Initializing GC with 64 KB heap...
[OK] Systems initialized
REPL ready. Type 'help' for commands.
> help
MicroCC REPL Commands:
ls - List .c files in embedded filesystem
cat <file> - Display file contents
compile <file> - Compile a C file
run - Execute the compiled code via JIT
demo - Run full demo (ls, cat, compile, run)
help - Show this help
quit / exit - Exit REPL
> ls
Files in embedded filesystem:
----------------------------------------
sample1.c 72 bytes
sample2.c 132 bytes
sample3.c 123 bytes
----------------------------------------
Total: 3 .c file(s)
> cat sample1.c
Contents of 'sample1.c':
----------------------------------------
int main(void) {
int a = 10;
int b = 20;
return a + b;
}
----------------------------------------
> compile sample1.c
Compiling sample1.c (72 bytes)...
SUCCESS: Compiled 28 bytes code, 0 bytes data
Code ready to run. Use 'run' to execute.
> run
Executing 28 bytes at 0x2000E000...
----------------------------------------
Result: 30
----------------------------------------
> cat sample2.c
Contents of 'sample2.c':
----------------------------------------
int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
int main(void) {
return factorial(5);
}
----------------------------------------
> compile sample2.c
Compiling sample2.c (132 bytes)...
SUCCESS: Compiled 64 bytes code, 0 bytes data
Code ready to run. Use 'run' to execute.
> run
Executing 64 bytes at 0x2000E000...
----------------------------------------
Result: 120
----------------------------------------
> cat sample3.c
Contents of 'sample3.c':
----------------------------------------
int fib(int n) {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2);
}
int main(void) {
return fib(7);
}
----------------------------------------
> compile sample3.c
Compiling sample3.c (123 bytes)...
SUCCESS: Compiled 72 bytes code, 0 bytes data
Code ready to run. Use 'run' to execute.
> run
Executing 72 bytes at 0x2000E000...
----------------------------------------
Result: 13
----------------------------------------
What's Happening
When you type compile sample1.c:
- Preprocessing — Handles
#include,#define, macros - Lexing — Tokenizes the C source code
- Parsing — Builds an Abstract Syntax Tree
- Type Checking — Validates types, handles promotions
- Code Generation — Emits ARM Thumb-1 or Thumb-2 machine code
- JIT Execution — Copies code to SRAM and jumps to it
All of this happens in ~10ms on the 133MHz Cortex-M0+ processor.
Technical Details
| Feature | Details |
|---|---|
| Target | ARM Cortex-M0+ (Thumb-1 instruction set) |
| Memory | 64KB GC heap, mark-and-compact collector |
| Code Size | ~26KB UF2 (including compiler + REPL + samples) |
| Self-Hosting | Yes — can compile its own source code |
Sample Programs
The REPL includes three sample programs:
| File | Description | Expected Result |
|---|---|---|
sample1.c |
Simple arithmetic (10 + 20) | 30 |
sample2.c |
Recursive factorial(5) | 120 |
sample3.c |
Recursive fibonacci(7) | 13 |