[mvt] RFR - add support for q-types in lambda forms

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Thu Jun 1 12:26:23 UTC 2017

please review the following patches which add support for q-types in 
lambda forms (LFs).


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 mailing list