[mvt] RFR - add support for q-types in lambda forms
maurizio.cimadamore at oracle.com
Fri Jun 2 14:44:11 UTC 2017
Here's a second revision of the jdk changes:
* I've shared the initialization of the static flag used by both
LambdaForm and MethodTypeForm in MethodHandleStatics.
* I've removed tabs from MVTTest
On 02/06/17 01:09, Maurizio Cimadamore wrote:
> On 02/06/17 01:00, Paul Sandoz wrote:
>>> On 1 Jun 2017, at 05:26, Maurizio Cimadamore
>>> <maurizio.cimadamore at oracle.com> wrote:
>>> please review the following patches which add support for q-types in
>>> lambda forms (LFs).
>> A minor point, it can make it easier to remember the flags if they
>> are grouped in a known place, such as MethodHandleStatics.
> Good suggestion, I'll do that
>> At least the test has some tabs vs. spaces, which results in
>> off-kilter formatting. Perhaps auto format the code in IntellJ?
> Yeah there's something up there...
>> For BMHs i am guessing we would require some expansion to include Q
>> in the set of support basic types e.g. Species_Q and appropriate
>> modifications to the code generation like you did for LamdaForm?
> I think one problem is to fix BMH/Species_Q and the classes it heavily
> relies on (LambdaFormEditor) to support Q-types. But the other flip of
> the coin is to handle APIs - if you have a method handle that takes a
> Q-type, how do you 'bind' its argument? The APIs we have today are
> boxy. Some of these APIs are used internally by some of the BMH
> classes, so you could hit them through combinators too.
>>> Adding q-type support in LFs is not straightforward, and we have
>>> played with a number of approaches. The big issue is that LFs are a
>>> mechanism to _share_ code between method handles (MHs) and they do
>>> so by means of 'erasure' - that is, two method handles taking String
>>> and Integer can (in principle) share the underlying LF (whose type
>>> is erased to Object - or L in the basic type lingo).
>>> With value types, erasure/sharing becomes an issue - on the one
>>> hand, we have to make sure that value types are not erased to the
>>> basic type L - which would then lead to nonsensical bytecode
>>> generation (such as areturn on a value operand). On the other hand
>>> there's a question of how much type information on value types we
>>> want to preserve during LF spinning. Two options are available:
>>> 1) value types do not get erased. That is, if a method type mentions
>>> one or more value types, such types are not erased in the underlying
>>> method form (MF)
>>> 2) a new basic type is added for value types (Q) and all value types
>>> 'erase' to that
>>> We started our experiments with (1) - while it is doable, there are
>>> two major pain points:
>>> * keeping sharp type information on value types inside LF
>>> effectively undoes one of the big advantages of LFs - that is, code
>>> sharing. A MH taking a QPoint and a MH taking a QPerson will _not_
>>> be able to share the underlying LF.
>>> * this approach is basically at odds with the rest of the LF
>>> machinery, which is fundamentally basic-type based. This is
>>> particularly evident in the handling of intrinsics (see
>>> LambdaForm.createFormsFor) where special LF (such as 'identity' or
>>> 'zero') are cached in an array that is indexed by basic type. If we
>>> followed this approach we would split the implementation in two:
>>> parts for which caching can be enabled, and parts for which caching
>>> is not possible.
>>> For these reasons, we moved on to consider (2). The most problematic
>>> aspect of (2) is to find a common type to which all values can be
>>> safely 'erased' to. Turns out that hotspot already has this type -
>>> that's what java.lang.__Value is used for. So, we can indeed define
>>> a new basic type for Q, and use the j.l.Class of j.l.Value as its
>>> standard representation (while Object.class is still used for the
>>> boxed representation).
>>> All the rest is can be regarded as implementation details - that is,
>>> we need to make sure that Q-types are erased accordingly when going
>>> to method types to MF - and we also need to make sure that LF with
>>> values in them generate the correct bytecode. This was a major
>>> implementation challenge, as the current InvokeBytecodeGenerator is
>>> written in terms of ASM which doesn't (yet) have support for value
>>> opcodes. To workaround the issue, I've written an alternate
>>> InvokeBytecodeGenerator - called LambdaFormBuilder which is
>>> specifically used to spin bytecode for LF containing values. This
>>> builder is based on the bytecode API already bundled with the
>>> valhalla repo.
>>> Now, to the practical bits :-)
>>> There are a bunch of flags I've added to enable the new support; the
>>> first and most important is:
>>> which allows value types to survive MethodType->MethodForm
>>> conversion, and enables the alternate LambdaFormBuilder class for
>>> spinning bytecode.
>>> There's also a second flag:
>>> Which can be used to allow constant pool patching in the
>>> MethodHandleBuilder machinery - this should bring the generated code
>>> for LambdaFormBuilder closer to what InvokeBytecodeGenerator was
>>> emitting. But it can be mostly regarded as an optional optimization.
>>> Finally, more work needs to be done in this area; first, this
>>> support only works if JIT is disabled (e.g. -Xint is used). Some
>>> work in this area is currently under way - see:
>>> Secondly, bound method handle are _not_ supported. It seems like
>>> adding support for Q-types here would require some major surgery to
>>> the underlying machinery (most of the signatures for BMH are
>>> expressed in terms of Object anyway - see insertArguments).
>>> I would like to thank Vladimir Ivanov - this work would not have
>>> been possible w/o his many thoughtful insights. I'd also like to
>>> thank Roland Westrelin for 'alpha-testing' this patch, a thankless
>>> job which led to many bug fixes.
More information about the valhalla-dev