Lworld and calling convention

Karen Kinnear karen.kinnear at oracle.com
Thu Apr 19 19:09:49 UTC 2018


Roland -

You can ignore my email below here - I had misunderstood what Frederic was suggesting.

Only addition to add to Frederic’s proposal - which has javac just annotate the declaring class
for a method - is that perhaps we can do the pre-loading of the classes for parameters annotated
as value classes when we link a class - so a bit lazily - since we need the information to create the
vtables and itables.

I do not know the C2 code. What little I have read implies that the static receiver class has to be
initialized or we uncommon trap. I don’t know if inlining decisions have the same prerequisite.

thanks,
Karen

> On Apr 19, 2018, at 9:27 AM, Karen Kinnear <karen.kinnear at oracle.com> wrote:
> 
> thanks,
> Karen
> 
>> On Apr 18, 2018, at 3:38 PM, Frederic Parain <frederic.parain at oracle.com> wrote:
>> 
>> Roland,
>> 
>> Thank you for this summary.
>> 
>> Would it help to have javac to annotate which arguments are expected
>> to be value classes? In this case:
>> - only classes of annotated arguments would be pre-loaded (no additional
>>   pre-loading for old code)
>> - after pre-loading, if the class is a value class, the calling convention could
>>   use scalarisation, otherwise argument is passed by reference
>> - arguments without the annotation would always be passed by reference
>> 
>> With this proposal, the calling convention would depend only on the
>> information provided by javac, not on the value of the arguments being
>> passed (passing null would not impact the determination of the calling
>> convention).
>> 
>> However, I’m not sure it would solve the issue with method inlining if the
>> method candidate for in-lining are not known before the compilation starts.
>> 
>> Fred
>> 
>> 
>>> On Apr 5, 2018, at 11:53, Roland Westrelin <rwestrel at redhat.com> wrote:
>>> 
>>> 
>>> 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