Version française
Home     About     Download     Resources     Contact us    
Browse thread
OCamlJIT2 vs. OCamlJIT
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: -- (:)
From: Benedikt Meurer <benedikt.meurer@g...>
Subject: Re: ocamlopt LLVM support (Was: [Caml-list] OCamlJIT2 vs. OCamlJIT)

On Dec 3, 2010, at 21:07 , Jon Harrop wrote:

> Benedikt wrote:
>> So, let's take that LLVM thing to a somewhat more serious level (which
>> means no more arguing with toy projects that simply ignore the
>> difficult stuff).
> 
> You can learn a lot from prototypes.

Indeed, but you need stay on topic. HLVM does not prototype the use of LLVM within OCaml, because you're using a quite different data representation.

Using a different data representation within OCaml would indeed be possible, but one has to ask what it's worth? You'd get better floating point performance (most likely) and possibly also gain from a few LLVM optimization passes. But on the other hand:

1. You will have to rewrite not only the compiler, the standard library and the bytecode interpreter (already a massive amount of work), but you also loose compatibility with almost every existing OCaml library binding, since the native function interface will be different. That's a hell of a lot of work for many people.

2. The current data representation is already close to optimal for symbolic computation, so there's is not much to gain here. The most complex applications use OCaml for symbolic computation (i.e. Coq), so they will not benefit (if at all, see 3.).

3. The current garbage collector is mostly straight-forward because of the data representation. No need to carry around/load type information, you just need the following bits of information: Is it a block in the heap? Does it contain pointers? If so how many? All this information is immediately available with the current data representation (also improves cache locality of the GC loops). Even if the current GC is replaced with something more recent (which I'm sure is worth it), the new GC will also be easy to implement because of the data representation.

4. Marshalling/unmarshalling will also be touched (and probably become more complex), loosing even more compatibility (less of an issue than the FFI, but nevertheless something to take care of).

So, it is really worth to spend years on a new data representation (including fixing all C bindings, etc.)? Just to get better floating point performance? Integer performance will be almost the same, avoiding the shift/tag bit just replaces an "addq r1, r2; subq $1, r2" with "addq r1, r2"; even doing this thousands of times will not cause a noticeable difference.

I already mentioned a few simple ways to improve floating point performance w/o the need to throw away most of the code base. I.e. just compile (tail-)recursive floating point functions in a way that avoids the boxing for the (tail-)calls. If this seems too complex, you could use an even simpler trick: Just compile every floating point function twice, one "generic version", which accepts "value"s (for use in closures, on parameter position, etc.), and one "float version" which takes non-boxed doubles. Calling the appropriate version is trivial, just pass the type information down to the Cmm level. That way everything will continue to work, no need to mess up everything. And you can also apply your LLVM optimizations.

>> 3. A better LLVM. If we do it right, other's implementing functional
>> languages using LLVM will not be faced with the same problems again.
>> Dunno if that's possible.
> 
> What exactly are you thinking of that goes beyond what the other functional
> languages that already target LLVM have already had to implement?

Two main areas for now: The GC interface and the exception handling. LLVM's exception support is really limited; the GC support is better and more generic. I don't know how to implement the OCaml exception model within LLVM w/o adding a lot of special case stuff to LLVM itself (mentioned in my original post); if there would be a way to easily extend LLVM with special exception models, other projects could plug in their models.

For the GC interface. From what I know right now, the "ocaml" GC plugin generates the frametables and the special symbols. But several details are left to LLVM, which don't seem to be handled properly: For example, there does not seem to be a way to tell LLVM how to spill/reload; in case of OCaml the stack cells marked as gcroots must not contain "untagged integers". I'm still trying to figure out what are the exact lowering mechanisms involved, so it may already be possible - we shall see.

The other thing I noticed so far: LLVM does not seem to eliminate stores to gcroots (probably required for Java, dunno). In case of ocamlopt, this would generate a lot of unnecessary stores. ocamlopt avoids this because it has knowledge not available to LLVM, i.e. when calling a native C floating point primitive (sin, cos, ...), ocamlopt knows that i.e. %r12 is preserved and the primitive does not allocate or raise so no need to spill the value to the stack cell, LLVM does not know this (and there's no way to tell, yet) so it has to spill and reload all (possible) gcroots.

You can think of various similar examples, where ocamlopt generates better code, simply because it "knows" OCaml (and even tho it lacks complex data/control analysis), whereas LLVM is generic (not necessarily a bad thing) but lacks ways to "learn" OCaml (or other language specific stuff). Adding more ways to tell LLVM about certain constraints will also be beneficial to other projects (not only functional programming languages...).

> Cheers,
> Jon.

Benedikt