What is it?
- Simple DSL over LLVM/QBE.
- Your language AST → petc IR → [LLVM / QBE / C] → binary.
- ~25 stack-based opcodes.
- Whole IR spec fits in 15 minutes of reading.
- Compiles to native code via LLVM, QBE, or C.
- Fast compilation, zero overhead.
- ~5700 lines in Crystal.
Why?
- I was writing my own language and got tired of fighting with LLVM IR. SSA, phi nodes, basic blocks — X(.
- Usually when you write your own language, you first build a parser and generate an AST. Then comes the hell stage — translating your AST into LLVM or another backend. Petc takes on all that complexity.
- LLVM is complex.
- Petc is simple and fun.
- Stack-based opcodes are easy to emit from AST with a simple one-pass tree walk.
- You’re not locked into one backend. LLVM for speed, QBE for fast compiles, C for anywhere.
Ultimate goal
Beat LLVM (joke). Real goal: beat gcc :).
Benchmark:
Mandelbrot renderer from mandel.bf (by Erik Bosman). All IR represent the same program. Shows whether Petc adds overhead over direct backend usage. Running on Macbook M1 in benchmark/brainfuck-compiler.
| IR | Compiler | IR size, Kb | Compile time | Run time |
|---|---|---|---|---|
| llvm | clang(-O3) | 1529 | 1303ms | 638ms |
| petc | petc-llvm(–release) | 443 | 1092ms | 613ms |
| qbe-ssa | qbe + clang(linker) | 345 | 241ms + 91ms | 771ms |
| petc | petc-qbe(–release) | 443 | 756ms | 786ms |
| c | clang(-O3) | 128 | 1420ms | 638ms |
| petc | petc-c(–release) | 443 | 1436ms | 636ms |
Quick Start (compile and run first program).
echo 'FUNC main BODY PUSH "Hello petc\n" PRINTF 0 ENDFUNC' | crystal src/cli/llvm.cr r
Status:
Alpha, but working. The whole compiler is ~5700 lines of Crystal (reads in an hour). 2900 tests pass. If you’re writing a language and don’t want to wrestle LLVM — try it out. Feedback welcome.