petc(PetCompiler) — a small IR for building languages over LLVM, QBE, C with zero overhead

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.

Petc on GitHub

2 Likes