RI update: division of bridging responsibility between VM and compiler

Remi Forax forax at univ-mlv.fr
Thu May 9 05:25:46 PDT 2013


On 05/08/2013 10:21 PM, Brian Goetz wrote:
>> I have a question, do we have an idea of the bridge methods that need to
>> be included in java.util classes becaue we have added default methods on
>> several interfaces ?
>
> We don't, but we should be able to measure that pretty easily -- write 
> an ASM program to crawl the JDK and emit a diagnostic for every 
> ACC_BRIDGE method it finds.

in b88, the program you suggest found no such bridges in java.util (or 
in the whole JDK).

>
>> As I currently understand the problem, I don't think there will be much
>> trouble for code that use the JDK.
>> But for code like Guava collection that uses JDK interfaces and are used
>> in application code, it will be more complex.
>> That said I don't know if Guava introduces new interfaces that use
>> covariant return type or bounded generics.
>
> Constructs like:
>
>   interface FooPredicate extends Predicate<Foo> { ... }
>
> seem likely enough in third party libraries.  In this case, javac 
> would generate the bridge into FooPredicate at compile time.

I more worry about existing third party libraries that uses already 
existing interfaces like List or Map
because these libraries may need to be re-compiled.
I agree that they need to be recompiled only in a corner case,
the third party library need to extends an already existing interface 
and have a public method
with a name and parameter types that make the method an override of a 
default method listed below,
but this corner case will appear.

java/util/concurrent/ConcurrentMap default 
getOrDefault(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
java/util/Comparator default reverseOrder()Ljava/util/Comparator;
java/util/Comparator default 
thenComparing(Ljava/util/Comparator;)Ljava/util/Comparator;
java/util/Map default 
getOrDefault(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
java/util/Map default 
putIfAbsent(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
java/util/Map default 
replace(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Z
java/util/Map default 
replace(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;

By example, a comparator defined like this will cause trouble:
public class FooComparator extends Comparator<Foo> {
   public FooComparator reverseOrder() { ... }
}

this code must be be recompiled because javac need to insert a bridge.

That said, a note about this in the compatibility issues guide of the 
jdk8 is enough in my opinion.

>
>
>>> Instead, we're now pursuing a path where we generate bridges into
>>> interfaces (since we can do that now) using an algorithm very similar
>>> to what we do with class bridges.  We may need to extend the technique
>>> of compiler-generated bridges with generating additional classfile
>>> attributes that the VM might act on to avoid these anomalies,
>>> currently being explored.
>>
>> I don't understand the last sentence ?
>> The bridges are needed by the VM, so what's the point of having the VM
>> ignore them ?
>
> The fundamental problem that bridges address is: the notion of 
> inheritance implemented by the Java language and by the JVM are 
> different.  What appears to be two methods to the JVM may or may not 
> be the "same" method in Java.
>
> Bridges paper over this difference by generating bytecode that the VM 
> dumbly interprets.  But if the VM *knew* that method A was a bridge 
> for method B, it could do a better job.  For example, imagine a VM 
> implementation where the vtable contained MHs. Instead of building 
> bytecode for method A that just does a (potentially virtual) call to 
> method B, we could populate the slot for A with B.asType(A).  This 
> could peel one layer off the call stack.

A bridge is a method which is not overriden usually (unless you override 
the type with a raw type),
so if the VM is able to inline the call in the wonderful world where no 
bridge is needed,
it should be able to inline the call in the real world with bridges.

As a trivia, there is a case where the de-virtualisation actually works,
and will not if bridges are not generated :
abstract class A<T> { abstract void m(T t) { ... } }
class B<T> extends A<T> { void m(T t) { ... } }
class C extends A<String> { void m(C c) { ... } }
class D extends C { void m(C c) { ... } }

for a callsite A<T> a = ... , a.m(t), here we have 3 methods B.m, C.m 
and D.m so if a is effectively a B a C or a D,
the VM will not de-virtualize it in the wonderful world without bridges. 
But because we have bridges,
a.m(t) can only call B.m(Object) or C.m(Object) [the bridge] and inside 
the bridge it will dispatch again
on C or D. The VM will generate a if/else for the first call and another 
if/else for the call inside the bridge
de-virtualising the hierarchy.

>
> The last sentence was appealing to the notion that we could tag the 
> bridge method with an attribute that made its semantics scrutable to 
> the VM, so the VM could use this information to optimize.  (Think 
> "symlink").

There is already the access modifier ACC_BRIDGE and bridge methods are 
so small that the VM always
try to inline them, so there is no need of a special marker IMO.

>
> We have no plans to act on this information immediately, but reifying 
> information about the difference between language-level inheritance 
> and VM-level inheritance seems better than throwing this information 
> away and then trying to reconstruct it.
>
>

Rémi



More information about the lambda-spec-observers mailing list