[lworld] RFR: javac support for Parametric VM, first stab
Maurizio Cimadamore
mcimadamore at openjdk.java.net
Tue Mar 9 11:55:24 UTC 2021
On Sun, 7 Mar 2021 00:30:42 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
Overall looks solid - I see that that there's no ClassReader support, which is probably ok given that we rely on (declaration) annotations to do the job.
src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/PoolWriter.java line 201:
> 199: * Linkage_info constant in the constant pool
> 200: */
> 201: int generateLinkage(Object s, Attribute.Compound parametricAnno) {
This can probably be simplified, by having this method return the constant to be added - e.g.
Pool.Constant wrapWithLinkageIfNeeded(...)
And then let clients call `writeIfNeeded`, so that if flow is also more consistent with other pool methods.
src/java.base/share/classes/java/lang/annotation/Parametric.java line 31:
> 29: */
> 30: @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR})
> 31: public @interface Parametric {
Since we depend on this during code generation, should this has runtime retention? Otherwise, with separate compilation, I don't think we're able to see that a class is parameteric (if we're using it from a different client) ?
-------------
PR: https://git.openjdk.java.net/valhalla/pull/364
More information about the valhalla-dev
mailing list