[LLVMdev] first two chapters for the ocaml bindings in svn

Jon Harrop jon at ffconsultancy.com
Tue Dec 30 06:57:02 PST 2008


On Tuesday 30 December 2008 13:58:09 Erick Tryzelaar wrote:
> On Sat, Sep 20, 2008 at 9:57 AM, Jon Harrop
>
> <jonathandeanharrop at googlemail.com> wrote:
> > I think your new OCaml tutorials and the original C++ ones are absolutely
> > brilliant!
>
> Thanks Jon. I'm sorry I missed this message. Now that you've had some
> more experience with llvm, would you be interested in adding to the
> doc some performance and GC stuff? I haven't personally explored that
> yet.

As much as I would love to, I have actually learned very little about this 
(assuming you mean performance of LLVM-generated code and the use of GCs with 
LLVM-generated code) and was even going to ask questions along these lines 
here.

I implemented a BF compiler from scratch in OCaml, described in the following 
OCaml Journal article:

http://ocamlnews.blogspot.com/2008/09/writing-bytecode-compiler-using-llvm.html

It turned out to be 2x slower than the C++ one in the LLVM distribution. I 
assume the reason is the C++ version manipulates the pointer into the BF heap 
directly whereas I indirected everything through int-indexed array lookups 
and LLVM failed to optimize that away. Another lesson is that enabling LLVM's 
optimization passes can make a big difference.

Other than that, I spent a few days over Christmas working on a new HLVM that 
I have described here:

http://flyingfrogblog.blogspot.com/2008/12/building-better-future-high-level.html

I think this is a very exciting project with great potential but it is a huge 
undertaking. I already have the following features:

. Values of primitive types: unit, int, float, static null-terminated strings.

. Compound types: mutable arrays.

. Arithmetic (+, -, *, /) over ints and floats.

. Comparison (<, <=, =, >=, >, <>) over ints and floats.

. Let bindings.

. If expressions.

. Function application with multiple arguments.

. Compound sequences of expressions.

. Fast calling convention for internal functions, including tail calls.

. C calling convention for external functions and a special case for C's 
(varargs) printf.

. Top-level function definitions.

. JIT compilation to high-performance native-code using LLVM.

I am in the process of implementing the shadow stack and a simple GC but I 
also want first-class function pointers, run-time types and algebraic 
datatypes before I release anything.

Early benchmarks suggest that performance is excellent:

Int Fibonacci:
OCaml: 1.11s
HLVM:  1.20s

Float Fibonacci:
OCaml: 6.10s
HLVM:  2.07s 2.9x faster!

Sieve of Eratosthenes:
OCaml: 14.9s
HLVM:   6.63s 2.2x faster!

Mandelbrot:
OCaml: 4.07s
HLVM:  1.11s 3.7x faster!

As you can see, this new LLVM-based compiler generates code that can be 
several times faster than ocamlopt's. I would like to keep it that way but 
adding a GC threatens that and some of LLVM's missing features (e.g. 
exceptions) may require costly workarounds. Regardless, I am convinced that 
LLVM is by far the best tool for the job and the OCaml bindings make it a joy 
to work with from a language that was specifically designed for writing 
compilers.

The performance characteristics surprised me. In particular, LLVM's 
optimization passes make no difference to the performance of my HLVM on any 
of my benchmarks. I assume the reason is that LLVM's direct code generation 
does an excellent job with pure code and the optimizers can do nothing 
further with my impure code (e.g. setting array elements).

However, I am adding a GC and performance will probably demand a moving 
collector. So I will need to be able to update pointers into the heap and the 
easiest way to do that is to refer to everything by pointer with value types 
on the system stack and reference types in the shadow stack. When the GC is 
invoked it can mutate the pointers in the shadow stack safe in the knowledge 
that they will be reloaded but minimal performance will (hopefully) be lost 
because LLVM's optimization passes will remove as many of the redundant loads 
and stores as possible.

Partly because I have no desire to code in C++ but also due to the absence of 
working examples, I am not building upon LLVM's GC infrastructure. Instead, I 
am constructing a shadow stack "for an uncooperative environment". This will 
allow me to implement everything I need from the comfort of OCaml and, I 
believe, performance will be adequate.

As soon as I have anything more interesting to say I will gladly augment the 
documentation but, until then, I have a lot more learning to do!

-- 
Dr Jon Harrop, Flying Frog Consultancy Ltd.
http://www.ffconsultancy.com/?e



More information about the llvm-dev mailing list