Lworld and calling convention

Roland Westrelin rwestrel at redhat.com
Thu Apr 5 15:53:16 UTC 2018


I started to work on bringing the calling convention from MVT to the
Lworld.

The first problem I hit is that when adapters are generated, classes of
the method's signature may not have been loaded yet so it's impossible
to tell which argument is a value and which is not.

I then looked at what it would take to make adapter generation lazy. I
have a prototype that seems to allow for lazy generation but then the
question is what are the events that should trigger adapter generation?

Going from interpreter to compiled code (where the i2c is needed) is
fairly simple. We don't need the i2c until compiled code exists. We
already load the classes of the signature before we trigger the
compilation of the method so generating the adapters at that point would
be fine.

Going from compiled code to the interpreter seems much trickier. Let's
say we compile m(). The compiler will trim some untaken paths and inline
some methods so it's only possible to tell what call sites will be in
the compiled m() as it's being compiled or once it's compiled. There's
no guarantee that the classes of the signatures of these call sites are
loaded when the call sites are compiled because, in legacy code, null
may be passed for a value argument.

We can't load classes as we compile the method because the compiler
threads don't perform class loading.

If compiled method m() has a call site:

m2(v1, v2)

and v1 and v2 are values (from 2 different classes), that call site may
be compiled as:

m2(v1.f1, v1.f2, .., v2.f1, v2.f2, ..)

(passing fields) if when the call site was compiled, classes for v1 & v2
are loaded.

or:

m2(v1, v2.f1, v2.f2, ..)

(passing a reference to v1) if the class for v1 was not loaded when the
call site was compiled

or:

m2(v1.f1, v1.f2, .., v2)

if the class for v2 was not loaded when the call site was compiled

or:

m2(v1, v2)

if both classes were not loaded when the call site was compiled.

So for a single call site with 2 value arguments, there are 4 ways of
compiling the call site.

We could generate the adapters the first time the call in compiled
method m() is executed (at call resolution time) but then we need to
generate an adapter that matches how the call site was compiled. How do
we know which of the 4 ways of performing the call it is?

As code executes and classes get loaded, we might need several adapters
generated. Maybe some m2() call site was compiled as:

m2(v1, v2)

but then one class got loaded so another one got compiled as:

m2(v1, v2.f1, v2.f2, ..)

and then another one as:

m2(v1.f1, v1.f2, .., v2.f1, v2.f2, ..)

and we need 3 different c2i adapters.

Add to this, that we can't deoptimize at call resolution time because
compiled code only has debug info to resume execution after the call.

I see 2 ways forward with this. Either we eagerly load classes of
signatures so we can generate the adapters eagerly as is done
currently. Or we refuse to compile a call site for which the classes of
the signature are not loaded (replace the call with an uncommon trap and
trigger class loading?). In that case, we would sometime drop perfectly
good code.

Comments?

Roland.



More information about the valhalla-dev mailing list