[lworld] RFR: javac support for Parametric VM, first stab [v2]
Maurizio Cimadamore
mcimadamore at openjdk.java.net
Fri Mar 12 12:40:25 UTC 2021
On Thu, 11 Mar 2021 19:16:44 GMT, Vicente Romero <vromero at openjdk.org> wrote:
>> This patch is the initial support for Parametric VM in javac. This is the first iteration of the prototype, so still a work in progress. In order to test the new code users need to pass the hidden option: `-XDsupportParametricVM` to javac. See the examples below for an illustration of the bytecode the compiler can generate now. Lets assume we have the following class annotated with the `@Parametric` annotation:
>>
>> import java.lang.annotation.*;
>>
>> @Parametric(id="PAIR")
>> class Pair<X, Y> {
>> @Parametric(id="PAIR")
>> X fst;
>> @Parametric(id="PAIR")
>> Y snd;
>>
>> Pair(X fst, Y snd) {
>> this.fst = fst;
>> this.snd = snd;
>> }
>>
>> Pair<Y, X> swap() {
>> return new Pair<>(snd, fst);
>> }
>>
>> @Parametric(id="make", kind=ParamKind.METHOD_ONLY)
>> static <U, W> Pair<U, W> make(U fst, W snd) {
>> return new Pair<>(fst, snd);
>> }
>>
>> @Parametric(id="setFst", kind=ParamKind.METHOD_AND_CLASS)
>> <Z> Pair<Z, Y> setFst(Z newFst) {
>> return null;
>> }
>> }
>>
>> After compilation this class will contain the following new entries in the constant pool:
>>
>> #22 = Parameter CLASS:0 // CLASS:0
>> #37 = Parameter METHOD_ONLY:0 // METHOD_ONLY:0
>> #44 = Parameter METHOD_AND_CLASS:0 // METHOD_AND_CLASS:0
>> also there will be several elements with the new attribute `Parametric` which will appear in both fields, the `Pair` class per-se and methods: `make` and `setFst`.
>>
>> Let's now assume we define a Client willing to use class `Pair`, it will need to define values to bound to the "holes" defined in class `Pair` using the `Parametric` annotation. For this class Client should use annotations `@LinkageClass` and or `@LinkageMethod` depending on the kind of hole in class `Pair` it is intending to bound to. Let's define class `Client` as:
>>
>> import java.lang.annotation.*;
>>
>> class Client {
>> @LinkageClass("linkageClass")
>> void linkClassParams() {
>> Pair<String, Integer> psi = new Pair<>("first", 2);
>> }
>>
>> @LinkageClass("linkageClass")
>> void linkClassParamsFieldAccess(Pair<String, Integer> psi) {
>> psi.fst = "field";
>> }
>>
>>
>> @LinkageMethod("linkageMethod1")
>> void linkMethodParams(Pair<String, Integer> psi) {
>> Pair<String, Integer> pair = Pair.make("hello", 1);
>> }
>>
>> @LinkageClass("linkageClass")
>> @LinkageMethod("linkageMethod2")
>> void linkClassAndMethodParams(Pair<String, Integer> psi) {
>> Pair<String, Integer> pss = psi.setFst("b");
>> }
>> }
>>
>> again compiling this class with the hidden option `-XDsupportParametricVM` we can find these new entries in the CP:
>>
>> #7 = Linkage #8:#9 // linkageClass:Pair
>> #8 = String #10 // linkageClass
>> #9 = Class #11 // Pair
>>
>> this Linkage CP entry is referred from method: `linkClassParams` as:
>>
>> void linkClassParams();
>> descriptor: ()V
>> flags: (0x0000)
>> Code:
>> stack=4, locals=2, args_size=1
>> 0: new #7 // Linkage linkageClass:Pair
>>
>> also:
>>
>> #25 = Linkage #8:#26 // linkageClass:Pair.fst:Ljava/lang/Object;
>> #26 = Fieldref #7.#27 // Pair.fst:Ljava/lang/Object;
>> #27 = NameAndType #28:#29 // fst:Ljava/lang/Object;
>>
>> referred from method: `linkClassParamsFieldAccess` the bytecode looks like:
>>
>> void linkClassParamsFieldAccess(Pair<java.lang.String, java.lang.Integer>);
>> descriptor: (LPair;)V
>> flags: (0x0000)
>> Code:
>> stack=2, locals=2, args_size=2
>> 0: aload_1
>> 1: ldc #23 // String field
>> 3: putfield #25 // Linkage linkageClass:Pair.fst:Ljava/lang/Object;
>> 6: return
>>
>> also:
>>
>> #32 = Linkage #33:#34 // linkageMethod1:Pair.make:(Ljava/lang/Object;Ljava/lang/Object;)LPair;
>> #33 = String #35 // linkageMethod1
>> #34 = Methodref #9.#36 // Pair.make:(Ljava/lang/Object;Ljava/lang/Object;)LPair;
>>
>> this time referred by method: `linkMethodParams` with bytecode:
>>
>> void linkMethodParams(Pair<java.lang.String, java.lang.Integer>);
>> descriptor: (LPair;)V
>> flags: (0x0000)
>> Code:
>> stack=2, locals=3, args_size=2
>> 0: ldc #30 // String hello
>> 2: iconst_1
>> 3: invokestatic #14 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
>> 6: invokestatic #32 // Linkage linkageMethod1:Pair.make:(Ljava/lang/Object;Ljava/lang/Object;)LPair;
>> 9: astore_2
>> 10: return
>>
>> and finally:
>>
>> #41 = Linkage #42:#43 // linkageMethod2:Pair.setFst:(Ljava/lang/Object;)LPair;
>> #42 = String #44 // linkageMethod2
>> #43 = Methodref #7.#45 // Pair.setFst:(Ljava/lang/Object;)LPair;
>>
>> in this last example it can be seen that Methodref at #43 is also pointing to #7 which, as commented above, is another Linkage_info entry in the CP.
>>
>> TIA, for the feedback and comments,
>> Vicente
>
> Vicente Romero has updated the pull request incrementally with one additional commit since the last revision:
>
> addressing review comments
I realized that the logic for adding parameter entries is not 100% correct - added some comments.
src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java line 887:
> 885: };
> 886: }
> 887: databuf.appendChar(poolWriter.putParameter(kind));
There is something fishy here: note that parameter have an "id" - which can be reused by other entries. That is, you can have 5 different @Parameteric annotations, referring to the same parameter ID. Wouldn't this create duplicate parameters in the constant pool?
As a quick hack - we could pretend that the parameter CP entry had a "name" so that, instead of doing `putParameter`, you do `putParameter(name)` and poolWriter will use existing uniqueness logic to determine if a new parameter has to be emitted.
src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/PoolConstant.java line 244:
> 242: @Override
> 243: public Object poolKey(Types types) {
> 244: return Integer.valueOf(kind);
This is not good enough - as it means that you can have only one parameter of each kind.
See also my other related comment on this topic. I think this Parameter entry should have a "fake" String id, so that we can give them a name, and distinguish between them; then you can just use the String id as a pool key.
-------------
PR: https://git.openjdk.java.net/valhalla/pull/364
More information about the valhalla-dev
mailing list