"Model 2" prototype implementation

Rémi Forax forax at univ-mlv.fr
Sat Aug 8 19:50:23 UTC 2015



Le 7 août 2015 22:09:14 CEST, Brian Goetz <brian.goetz at oracle.com> a écrit :
>Sure, we could have chosen that.  But this is a more straightforward 
>translation, and the simpler the translation is, the fewer dark corners
>
>there are.  

Adding synthetic interfaces to several user defined classes/interfaces is not something simple too.

> For one thing, it means that instanceof/cast

yes, may be. But i fail to see why List<any> need to be reified.

 (and even more
>
>importantly, their reflective equivalents) *just work*.  

I'm not sure that adding the syntax Foo<any>.class to Java is a good idea. Currently, it's a syntax error.
People will ask why Foo<any>.class is allowed but not Foo<?>.class

> It means that 
>the subtyping is scrutable to the JIT and can feed information into 
>optimizations.

JITs tend to use actual classes of the receiver instead of the declared type and Hotspot doesn't do CHA on interfaces.

>
>Most dispatch on Foo<any> receivers can proceed without indy; the 
>prototype happens to use it for dispatching all calls on Foo<any> 
>receivers, but this is neither necessary nor optimal.

Pick your poison, either you add an indy at callsite or you add bridges like methods (they aren't bridge methods because it's legal to have a user defined method that override them) to each classes and variants that are any-fied.

Indy can not be used too early when the VM is booting and is slow until being JITed. Bridge like methods mean more bloat to every anyfied classes for every variant (int/long/double/etc).

Also with bridge like methods, the boxing is done by the caller in bytecode (not different from a user requested boxing for the JIT POV) while with indy boxing is done at callsite (better for escape analysis) using asType() which doesn't require to preserve identity (better for partial escape analysis).

regards,
Rémi 

>
>
>
>On 8/7/2015 2:01 PM, Rémi Forax wrote:
>> Hi Brian,
>> why do you need the interface Box$any and not use Object and send
>Box.class as a bootstrap argument to the indy call ?
>>
>> Rémi
>>
>>
>> Le 7 août 2015 17:49:31 CEST, Brian Goetz <brian.goetz at oracle.com> a
>écrit :
>>> Great question.  We're working on a writeup of the translation
>details;
>>>
>>> we announced the prototype before the writeup was ready, because we
>>> didn't want to keep early adopters from trying it out any further,
>but
>>> there's a writeup coming.
>>>
>>> Here's a very brief taste (but you'll have to wait for the writeup,
>or
>>> use javap on the output of javac, for more).  Imagine the function
>>>
>>>     R : Type -> Class
>>>
>>> which maps a compile-time type to a runtime class.  In Java 5
>generics,
>>>
>>> for a class Foo<T>, the R mapping is very simple -- everything is
>>> erased:
>>>
>>> R(Foo<String>) = Foo
>>> R(Foo<?>) = Foo
>>> R(Foo) = Foo
>>>
>>> In Valhalla, for a class Foo<any T>, the mapping is more complex:
>>>
>>> Compatibility demands we adopt the above mappings:
>>> R(Foo<String>) = Foo
>>> R(Foo<ref>) = Foo
>>> R(Foo<raw>) = Foo
>>>
>>> And we get some new ones:
>>> R(Foo<int>) = Foo${0=I} *
>>> R(Foo<any>) = Foo$any **
>>>
>>> *Name mangling is temporary; something better is in the works.
>>> **Foo$any is a synthetic *interface* generated by javac, which can
>be
>>> implemented by Foo<int> and Foo<String> alike (using boxing in the
>API
>>> where necessary).
>>>
>>> Because Foo$any is an interface, it can't have fields.  So we lift
>the
>>> fields of Foo to (possibly boxing) accessors on Foo$any (VM help is
>>> needed for nonpublic members here), and we convert field accesses
>>> (through receivers of type Foo<any> only!) to invocations of those
>>> accessors.  (Field access through concretely-typed receivers
>>> (Foo<String> or Foo<int>) are still translated with straight
>>> getfield/putfield, so the boxing penalty is only paid by wildcard
>>> users.)
>>>
>>> Here's your example:
>>>
>>>      public static void main(String[] args) {
>>>          Box<int> box = new Box<int>(3);
>>>          Box<any> box_any = box;
>>>          System.out.println(box_any.get());
>>>          System.out.println(box_any.t);
>>>      }
>>>
>>> And the bytecode (which works as expected):
>>>
>>> public static void main(java.lang.String[]);
>>>      Code:
>>>         0: new           #2                  // class "Box${0=I}"
>>>         3: dup
>>>         4: iconst_3
>>>         5: invokespecial #3                  // Method
>>> "Box${0=I}"."<init>":(I)V
>>>         8: astore_1
>>>         9: aload_1
>>>        10: astore_2
>>>        11: getstatic     #4                  // Field
>>> java/lang/System.out:Ljava/io/PrintStream;
>>>        14: aload_2
>>>        15: invokedynamic #5,  0              // InvokeDynamic
>>> #0:get:(LBox$$any;)Ljava/lang/Object;
>>>        20: invokevirtual #6                  // Method
>>> java/io/PrintStream.println:(Ljava/lang/Object;)V
>>>        23: getstatic     #4                  // Field
>>> java/lang/System.out:Ljava/io/PrintStream;
>>>        26: aload_2
>>>        27: invokedynamic #7,  0              // InvokeDynamic
>>> #1:t$get:(LBox$$any;)Ljava/lang/Object;
>>>        32: invokevirtual #6                  // Method
>>> java/io/PrintStream.println:(Ljava/lang/Object;)V
>>>        35: return
>>>
>>> BootstrapMethods:
>>>    0: #26 invokestatic
>>>
>java/lang/invoke/VirtualAccess.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
>>>      Method arguments:
>>>        #27 invokeinterface Box$$any.get:()Ljava/lang/Object;
>>>    1: #26 invokestatic
>>>
>java/lang/invoke/VirtualAccess.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
>>>      Method arguments:
>>>        #31 invokeinterface Box$$any.t$get:()Ljava/lang/Object;
>>>
>>> The invokedynamic at bci=15 and 27 invoke the get() and t$get()
>methods
>>>
>>> of the Foo$any interface.  We use invokedynamic instead of
>>> invokeinterface because it allows us to do some boxing adaptations
>more
>>>
>>> easily than statically generating yet more bridge methods.  (The
>>> VirtualAccess bootstrap simply does a MethodHandle.asType adaptation
>of
>>>
>>> the provided MH; in the current program, there is no difference in
>the
>>> signatures so it's as if we did an invokeinterface of Box$$any.get()
>or
>>>
>>> .t$get() directly.)
>>>
>>>
>>>> I’m wondering how will the implementation work?
>>>> Specifically, in this example, which assumes a specialized |Box|
>>> class
>>>> defined here
>>>>
>>>
><web.archive.org/web/20150801070114/http://cr.openjdk.java.net/~briangoetz/valhalla/specialization.html#erasure-example-a-simple-box-class>:
>>>>
>>>> |Box<int> box = new Box<int>(4); Box<any> box_any = box; // what
>does
>>>> get() return? and how does it return it?
>>>> System.out.println(box_any.get()); // even more interesting, how
>does
>>>> this work: System.out.println(box_any.t /* assuming the box value
>is
>>>> visible */); |
>>>>
>>>> How do you translate the method calls and field accesses? Do they
>>> return
>>>> boxed values?
>>>>
>>>> Thanks,
>>>> Vlad
>>>>
>>>> PS: Shameless advertising — I recently worked on a paper describing
>>> the
>>>> pitfalls I’ve encountered in miniboxing
><http://scala-miniboxing.org>
>>>> when using erased views (and specialized views) and how to mitigate
>>>> them, which has been accepted for PPPJ 2015. In case you’re
>>> interested,
>>>> an earlier draft is available here
>>>> <http://infoscience.epfl.ch/record/208797?ln=en>.
>>>>
>>>>>>



More information about the valhalla-dev mailing list