Possibly inefficient code gen of instanceof patterns

Claes Redestad claes.redestad at oracle.com
Thu Jan 28 13:58:47 UTC 2021


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);
         }
     }

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?

- 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?)

- 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.

My gut feeling - without knowing much about javac - is that at least
the use of a temporary slot (5) could be optimized away here.

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


More information about the compiler-dev mailing list