Limitations of the Calling Convention Optimization

Brian Goetz brian.goetz at oracle.com
Wed Oct 21 17:14:04 UTC 2020


What I am hoping to get out of this discussion is a better understanding 
of what API design guidelines we have to give to developers.  The 
standard advice is "use interfaces in external APIs", and there are very 
good reasons for this. Interfaces make no commitment as to 
implementation, allowing implementations to evolve.  Maybe there's one 
implementation now and multiple later; maybe there are multiple now and 
one later; interfaces give us this freedom (and sealing further gives us 
the freedom to control this destiny.)

If we have to tell people "if you ever might want to use a primitive 
class to implement X, you must do that up-front, burn that into your 
APIs, and permanently give up on ever having multiple implementations", 
this is a different story, and obviously more limiting.

I think Maurizio had been (reasonably) assuming that, if there was just 
one inline implementation, we would be able to get some reasonable 
baseline of performance expectations in the sealed interface + primitive 
class implementation world, allowing us to continue with the API design 
principles that have stood us well for decades.  But it sounds like 
we're not sure about that now.

To be clear, this is bigger than calling-convention optimizations; it's 
about our goals for what language we want to build; it seems like we 
have been assuming some things that are not quite valid, and its 
important to understand the implications and whether the requirements 
need to be adjusted.


On 10/21/2020 12:34 PM, John Rose wrote:
> On Oct 21, 2020, at 5:02 AM, Tobias Hartmann <tobias.hartmann at oracle.com> wrote:
>> Also, the VM would need to ensure that the argument/return type is eagerly loaded when the adapters
>> are created at method link time (how do we even know eager loading is required for these L-types?).
> This is the hard kernel of the problem.  The L in LFoo; means
> “Lazy Loading” (among other things); you simply cannot assume
> that Foo.class will be loaded when you are setting up layouts and
> calling sequences for an LFoo;.  To get that information (and more)
> we will always need a signal somewhere in the call site which says,
> “do the preload” (which is what I call the “go and look” signal).
> The Q in QFooVal; performs this duty.
>
> That said, we might have wiggle room with sealed interfaces.
> If there is a “go and look” signal (QFooVal;) nearby that has
> forced the loading of an inline type FooVal which has an
> interface type FooRef as a super, *and* if FooRef is sealed,
> then we can go to work on FooRef calling sequences, if we
> think it’s profitable.  (We could substitute in a null flag plus
> a FooVal instance, and we can try to merge the null flag into
> the “guts” of the FooVal registers, if there is slack.  Or pass
> the FooVal on stack by reference.  Or other tricks like that.)
>
> But, back to the hard kernel of the problem, if there is no
> mention of QFooVal; near the LFooRef; descriptor, all bets
> are off; FooRef.class must be assumed to be unavailable in
> the general case when forming calling sequences involving
> LFooRef;.
>
> The laziness of LFoo; gives it opaqueness, abstraction.  All you
> know is it’s a pointer; you can’t know (in general) what it
> points to until Foo.class is loaded and there’s an instance
> of Foo running around.  Before that it’s just nulls.  The
> opaqueness of LFoo; in turn means that you don’t know
> whether it is just a GC heap reference or something special
> (a disguised value).  When we try to do special side-heap
> treatment for LFoo; values that we discover are disguised
> values, we find that it’s difficult to contain the uses of
> those values; they are so nondescript they mix with
> Object pointers.  This creates a kind of pollution where
> suddenly every pointer that might be handed to the GC
> has to be checked to see if it’s a side-heap pointer (e.g.,
> thread local or on-stack). This possibility of pollution
> is introduced by the abstractness of LFoo; and the
> result of it is hard-to-control extra expenses wherever
> an opaque pointer must be stored in the real GC heap.
>
> Maybe we can slim those expenses down in the future,
> using ZGC-like colored pointers.  For now, I think the
> good thing to do is buffer on the real GC heap, as we
> do now.  Maybe the performance can be increased in
> the future by using side-heap tricks, after we figure
> out how to prevent side-heap pointers from polluting
> the real GC heap.
>
> It’s an odd linkage:  the Lazy Loading LFoo;, by its
> nature, has surprising connections to the GC.
>
> — John




More information about the valhalla-dev mailing list