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