The Great Startup Problem

Charles Oliver Nutter headius at
Mon Aug 25 13:15:02 UTC 2014

On Mon, Aug 25, 2014 at 4:32 AM, Marcus Lagergren
<marcus.lagergren at> wrote:
> LambdaForms were most likely introduced as a platform independent way of implementing methodhandle combinators in 8, because the 7 native implementation was not very stable, but it was probably a mistake to add them as “real” classes instead of code snippets that can just be spliced in around the callsite. (I completely lack history here, so flame me if I am wrong)

That's how I remember it, yes. The native impl was not only a bit was a security black hole because of all the
special-casing for method handles in the JIT, and it had serious
issues tracking type information correctly (infamous NCDFE problem).
LFs aren't perfect, but we are way better off now than we were with
that implementation.

I do remember a conversation I had with Chris Thalinger about how it
seemed wrong that method handles were treated as a middle grey area
between call site and target, potentially not inlining in either
direction. My suggestion was to treat all handles bound into a call
site as though they were simply added bytecode in the surrounding
method...essentially, force inline non-direct handles into the caller
immediately (for some definition of "immediately") and let the only
remaining decision be which DMHs to inline as well. It worked ok for
simple cases we tried, but there were some places it didn't work well.
I don't remember the details.

We also did a rough equivalent to indy for JRuby's dispatch, but
supported on any JVM:

* All Ruby-callable methods have unique generated invoker class for
arities 0-3,N. These invokers contained all argument adaptation, heap
frame management, etc...just like a force-compiled MH chain.
* Each call site gets a synthetic method body that does lookup,
caching, and dispatch. Dispatch passes directly into those 0-3,N call
paths, and for matching arities it should inline straight through (the
invokers implement all direct-path arities as direct calls to the
appropriate code).

These generated call site methods were only monomorphic, but this
setup gave us fully inlinable dynamic dispatches without indy. It
worked well if we bumped up inlining thresholds (this was
pre-incremental JIT) but we shelved it at the time. However, I'm
probably going to explore this path again to get near-indy speeds on
non-indy JVMs for the new IR-based JIT.

Put a bit more directly: I can generate a load of bytecode to get
indy-like behavior with or without indy too. The gulf between the
current indy implementation and my way – explicitly generating code
where and when I need it – is LambdaForm interpretation and

> For 9, it seems that we need a way to implement an indy that doesn’t result in class generation and installation of anonymous runtime classes. Note that _class installation_ as such is also a large overhead in the JVM - even more so when we regenerate more code to get more optimal types. I think we need to move from separate classes to inlined code, or something that requires minimium bookkeeping. I think this may be subject to profile pollution as well, but I haven’t been able to get my head around the ramifications yet.

I am going to play with the property Jochen mentioned, which forces
LFs to JIT much sooner. I feel like we're almost where we need to be,
but it feels like LFs need to be more directly represented as IR in C2
rather than going through this foggy middle ground of JVM bytecode.
*I* can do foggy JVM bytecode...indy should be doing a lot better than

Hell, should MethodHandle be backed by Graal IR instead of LFs? It
would still be interpretable, but when we go to JIT the chain we're
losing a lot less in translation, and we can do site or
target-specific specialization at that point.

I always saw MHs as a general-purpose call site IR. Maybe we should
make good on that.

- Charlie

More information about the mlvm-dev mailing list