Tell us about your compiler!

Nice! I will continue with C# for now, but maybe a future rewrite in F# would be a good start.

Thanks for catching this! Thinking in how to fix it.

1 Like

I am using OCaml to write an ML compiler targeting armv8 on my Raspberry Pi 4 with stock 32-bit OS. I am documenting the experience and trying to maintain an entire lineage here.

My struggle is mostly finding time. My success is that my 161-line compiler can compile and run:

let sqr = fun x -> x*x in (fun f -> f(f 3)) sqr
2 Likes

@jdh30 looks like youā€™ve got a lot of interesting things going on: a custom calling convention, an interesting way of keeping track of the environment, and you avoid libc. Looking forward to see your next steps!

Going to be using Rust to create a custom statically typed language. Been working through creating compilers that emit bytecode but Iā€™m SUPER excited to be able to explore emitting raw assembly :slight_smile:

2 Likes

Having put quite some effort into my hobby compiler I have come to the conclusion that my approach was entirely upside-down and the result was poor performance. Specifically I was using a stack-based compiler to compile a relatively sophisticated language into a relatively wide variety of assembly instructions. This resulted in far too many loads and stores and performance closer to ocamlcā€™s interpreted bytecode than ocamloptā€™s native code. Specifically, my generated code was 5.3x slower than ocamloptā€™s! This left me disappointed and frustrated: why bother generating native code that runs as slowly as interpreted bytecode?! Then I had a revelationā€¦

Especially if youā€™re targeting the register-rich Arm architectures the initial focus should be restricted to:

  • Int constants
  • Calling conventions
  • Tail call elimination
  • Conditional execution
  • Register allocations

i.e. not arithmetic, loads, stores, strings, globals and so on.

Why? Because you can implement everything with just this by relegating all non-essential functionality to a C stdlib. Arithmetic operations become functions. Loads and stores become functions. Even stdin and stdout are obtained via function calls. You can call any C function!

Using this approach I have managed to implement a compiler for a general purpose programming language that generates just 7 different assembly instructions and the entire compiler is just 202 lines of OCaml code (+68 more for lexing and parsing) and the generated code is ā€œonlyā€ 2.9x slower than ocamlopt.

What do you think?

Iā€™ve written an echo program and Fibonacci function in my current source language. Next Iā€™ll try implementing a more sophisticated front end that targets this minimal language.

1 Like

@jdh30 thanks for the interesting experience report! You original experiment is located in the following repo, right?

GitHub - jdh30/growing_a_compiler: Growing a compiler

Is the new compiler available publicly?

Thatā€™s the old one, yes. I havenā€™t made the new one public yet but Iā€™d like to but, to be honest, Iā€™m getting tired of all the tedium around VCS these days so Iā€™m working on something better. Maybe Iā€™ll put it up there. :slight_smile:

Also, Iā€™d like to bootstrap itā€¦

I forgot -O2 when compiling my C stdlib which actually makes a big difference: my compiler generates code that is 2.25x slower than ocamlopt on both Fibonacci and Hailstones benchmarks!

Also worth noting that those benchmarks are basically the best case scenario for OCaml at this point.

1 Like

Hi,

The two languages I have implemented:

A JavaScript interpreter: https://code.google.com/archive/p/sejscript implemented in Delphi, using bytecode.

A transpiler from NoSQL to SQL: http://jsegarra.net/nosqlonsql , implemented in C#.

1 Like