Possibly inefficient code gen of instanceof patterns

Claes Redestad claes.redestad at oracle.com
Thu Jan 28 15:24:02 UTC 2021


Thanks, Jan!

/Claes

On 2021-01-28 16:11, Jan Lahoda wrote:
> Hi,
> 
> I've filled:
> 
> https://bugs.openjdk.java.net/browse/JDK-8260593
> 
> 
> Thanks,
> 
>      Jan
> 
> 
> On 28. 01. 21 15:25, Remi Forax wrote:
>> ----- Mail original -----
>>> De: "Claes Redestad" <claes.redestad at oracle.com>
>>> À: "compiler-dev" <compiler-dev at openjdk.java.net>
>>> Envoyé: Jeudi 28 Janvier 2021 14:58:47
>>> Objet: Possibly inefficient code gen of instanceof patterns
>>> Hi,
>>>
>>> as an experiment, I changed some code to use instanceof pattern
>>> matching to see if there's any effect on the generated code.
>>>
>>> Consider j.l.i.BootstrapMethodInvoker.invoke, which I changed from:
>>>
>>>      private static Object invoke(MethodHandle bootstrapMethod, Lookup
>>> caller,
>>>                                   String name, Object type) throws
>>> Throwable {
>>>          if (type instanceof Class) {
>>>              return bootstrapMethod.invoke(caller, name, (Class)type);
>>>          } else {
>>>              return bootstrapMethod.invoke(caller, name, 
>>> (MethodType)type);
>>>          }
>>>      }
>>>
>>> .. to:
>>>
>>>      private static Object invoke(MethodHandle bootstrapMethod, Lookup
>>> caller,
>>>                                   String name, Object type) throws
>>> Throwable {
>>>          if (type instanceof Class c) {
>>>              return bootstrapMethod.invoke(caller, name, c);
>>>          } else {
>>>              return bootstrapMethod.invoke(caller, name, 
>>> (MethodType)type);
>>>          }
>>>      }
>> It should be "type instanceof Class<?> c" BTW.
>>
>>> This seem to grow the bytecode size ever so slightly. Looking at the
>>> javap output it seems some of that might be redundant and possibly
>>> fixable:
>>>
>>>    private static java.lang.Object invoke(java.lang.invoke.MethodHandle,
>>> java.lang.invoke.MethodHandles$Lookup, java.lang.String,
>>> java.lang.Object) throws java.lang.Throwable;
>>>      descriptor:
>>> (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; 
>>>
>>>      flags: (0x000a) ACC_PRIVATE, ACC_STATIC
>>>      Code:
>>>        stack=4, locals=6, args_size=4
>>>           0: aload_3
>>>           1: astore        5
>>>           3: aload         5
>>>           5: instanceof    #46                 // class java/lang/Class
>>>           8: ifeq          27
>>>          11: aload         5
>>>          13: checkcast     #46                 // class java/lang/Class
>>>          16: astore        4
>>>          18: aload_0
>>>          19: aload_1
>>>          20: aload_2
>>>          21: aload         4
>>>          23: invokevirtual #169                // Method
>>> java/lang/invoke/MethodHandle.invoke:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object; 
>>>
>>>          26: areturn
>>>          27: aload_0
>>>          28: aload_1
>>>          29: aload_2
>>>          30: aload_3
>>>          31: checkcast     #61                 // class
>>> java/lang/invoke/MethodType
>>>          34: invokevirtual #172                // Method
>>> java/lang/invoke/MethodHandle.invoke:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/Object; 
>>>
>>>          37: areturn
>>>        LineNumberTable:
>>>          line 226: 0
>>>          line 227: 18
>>>          line 229: 27
>>>        LocalVariableTable:
>>>          Start  Length  Slot  Name   Signature
>>>             18       9     4     c   Ljava/lang/Class;
>>>              0      38     0 bootstrapMethod
>>> Ljava/lang/invoke/MethodHandle;
>>>              0      38     1 caller
>>> Ljava/lang/invoke/MethodHandles$Lookup;
>>>              0      38     2  name   Ljava/lang/String;
>>>              0      38     3  type   Ljava/lang/Object;
>>>        StackMapTable: number_of_entries = 1
>>>          frame_type = 27 /* same */
>>>      Exceptions:
>>>        throws java.lang.Throwable
>>>
>>> - The storing and loading of the type argument into slot 5 seem
>>> completely redundant: couldn't we load slot 3 in each place that
>>> now loads slot 5?
>> yes, it looks like a bug.
>>
>>> - Storing c into a new slot 4 also seem unfortunate, but is more
>>> understandable (I guess this is a limitation of the LVT not allowing
>>> overlapping a new variable with a new type into the same slot?)
>> The new syntax declares a supplementary local variable so it has to be 
>> visible in the bytecode for debuggers.
>>
>>> - It's also unfortunate that the checkcast now bubbles up so that the
>>> result has to be stored and loaded again, but I guess pushing the
>>> checkcast down to just before the invokevirtual as in the other
>>> branch would go against some specification.
>> Modern javac (i don't know the previous version, before java 1.4) 
>> never had such "optimization".
>>
>> As a grey beard, when the enhanced for was introduced, the for(:), we 
>> had the very similar discussions, because the enhanced for introduces 
>> a supplementary variable and usually a cast because of the erasure of 
>> generics, so the bytecode is a little more verbose. In the end, yes, 
>> it inflates the generated bytecodes at bit because it creates a new 
>> local variable but the java code is more readable because it creates a 
>> new local variable :)
>>
>>> My gut feeling - without knowing much about javac - is that at least
>>> the use of a temporary slot (5) could be optimized away here.
>> I agree.
>>
>>> WDYT?
>>>
>>> For reference the baseline method without the pattern matching
>>> compiles to this:
>>>
>>>    private static java.lang.Object invoke(java.lang.invoke.MethodHandle,
>>> java.lang.invoke.MethodHandles$Lookup, java.lang.String,
>>> java.lang.Object) throws java.lang.Throwable;
>>>      descriptor:
>>> (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; 
>>>
>>>      flags: (0x000a) ACC_PRIVATE, ACC_STATIC
>>>      Code:
>>>        stack=4, locals=4, args_size=4
>>>           0: aload_3
>>>           1: instanceof    #46                 // class java/lang/Class
>>>           4: ifeq          18
>>>           7: aload_0
>>>           8: aload_1
>>>           9: aload_2
>>>          10: aload_3
>>>          11: checkcast     #46                 // class java/lang/Class
>>>          14: invokevirtual #169                // Method
>>> java/lang/invoke/MethodHandle.invoke:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object; 
>>>
>>>          17: areturn
>>>          18: aload_0
>>>          19: aload_1
>>>          20: aload_2
>>>          21: aload_3
>>>          22: checkcast     #61                 // class
>>> java/lang/invoke/MethodType
>>>          25: invokevirtual #172                // Method
>>> java/lang/invoke/MethodHandle.invoke:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/Object; 
>>>
>>>          28: areturn
>>>        LineNumberTable:
>>>          line 226: 0
>>>          line 227: 7
>>>          line 229: 18
>>>        LocalVariableTable:
>>>          Start  Length  Slot  Name   Signature
>>>              0      29     0 bootstrapMethod
>>> Ljava/lang/invoke/MethodHandle;
>>>              0      29     1 caller
>>> Ljava/lang/invoke/MethodHandles$Lookup;
>>>              0      29     2  name   Ljava/lang/String;
>>>              0      29     3  type   Ljava/lang/Object;
>>>        StackMapTable: number_of_entries = 1
>>>          frame_type = 18 /* same */
>>>      Exceptions:
>>>        throws java.lang.Throwable
>>>
>>> Thanks!
>>>
>>> /Claes
>> Rémi


More information about the compiler-dev mailing list