Working on a runtime & cancelling needless components

After reading some more (mostly on the net, but also in books, especially Engineering a Compiler & Modern Compiler Implementation in ML) and wrapping my mind around the whole subject of the internal structure of compilers more and more in detail (especially regarding SSA), I started realizing that there is not much need for several components that I was planning to create for the owlisp compiler.

For example it does not make much sense to compile to a custom virtual machine representation first. There is probably not much gain from this, keeping in mind that the declared final target is LLVM.

I was also thinking about transforming the source code into a control flow graph representation, but I don’t think this is necessary either, since transforming into LLVM-IR is implicitly doing this, so there is no need to develop data structures and algorithms explicitly inside of the owlisp compiler.

I also cancelled the plan to target Parrot VM in a first step. I have come to the conclusion that targeting LLVM from the beginning on will most probably lead to something useful more quickly, since I don’t have to think about abstraction layers that much.

Once I have a vertically functional basis (i.e. a compiler that supports only a small subset of the language, but can compile these few known language-constructs successfully from source code to working target binary – talking about MVP 😉  ), I can once again think about if I want to insert an abstraction layer in case it is desired to target other platforms besides LLVM.

What I established though, is a runtime for owlisp. This seems to be necessary to me, since Common Lisp is a dynamically typed language which means that the type system must be available at runtime.

I have written the implementation of the runtime in plain C, since it is easy to transform it into LLVM-IR code and thus “link” it into a resulting binary.

The runtime is rather small currently – the only things that I have included initially are a few structures and correspoding functions for handling values & activation frames:

  • value_t – structure for holding a value of any possible type
  • frame_t – structure for holding an activation frame that contains (currently hard-coded up to 16 values) and a reference to an optional parent frame
  • new_value_int – allocates a value_t instance for an integer value
  • free_value – free a value_t instance
  • values_equal – compares two value_t instances
  • new_frame – allocates a frame_t instance
  • free_frame – frees a frame_t instance
  • set_binding – sets a binding in a frame_t to a given value
  • get_binding – gets the value for a binding from a frame_t instance

So, currently I am working on forging the chain from the evaluator (the high-level layer that “understands” Common Lisp expressions) to the low level LLVM api (which generates LLVM-IR code). For that, I have created an intermediate module (in llvm-ir.lisp) that abstracts away the LLVM details and is effectively a “melting pot” in the sense that it makes use of both the mentioned runtime functions as well as the low level LLVM api calls.

I’ll probably address details of interfacing to LLVM in another post.