8164451: Generate all zero and identity forms at link time
authorredestad
Fri, 19 Aug 2016 13:50:03 +0200
changeset 40419 20e2e4b25a40
parent 40418 fb874cf24974
child 40420 2e02ba62a678
child 40447 99f45c5df3f2
8164451: Generate all zero and identity forms at link time Reviewed-by: shade, mhaupt, vlivanov
jdk/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java
jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java
jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangInvokeAccess.java
jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java
--- a/jdk/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java	Fri Aug 19 10:03:43 2016 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java	Fri Aug 19 13:50:03 2016 +0200
@@ -38,6 +38,34 @@
  */
 class GenerateJLIClassesHelper {
 
+    static byte[] generateBasicFormsClassBytes(String className) {
+        ArrayList<LambdaForm> forms = new ArrayList<>();
+        ArrayList<String> names = new ArrayList<>();
+        HashSet<String> dedupSet = new HashSet<>();
+        for (LambdaForm.BasicType type : LambdaForm.BasicType.values()) {
+            LambdaForm zero = LambdaForm.zeroForm(type);
+            String name = zero.kind.defaultLambdaName
+                    + "_" + zero.returnType().basicTypeChar();
+            // since zero is the same as identity for Void, we deduplicate
+            // aggressively to guard against this specifically and not get
+            // caught on future equivalences
+            if (dedupSet.add(name)) {
+                names.add(name);
+                forms.add(zero);
+            }
+            LambdaForm identity = LambdaForm.identityForm(type);
+            name = identity.kind.defaultLambdaName
+                   + "_" + identity.returnType().basicTypeChar();
+            if (dedupSet.add(name)) {
+                names.add(name);
+                forms.add(identity);
+            }
+        }
+        return generateCodeBytesForLFs(className,
+                names.toArray(new String[0]),
+                forms.toArray(new LambdaForm[0]));
+    }
+
     static byte[] generateDirectMethodHandleHolderClassBytes(String className,
             MethodType[] methodTypes, int[] types) {
         LambdaForm[] forms = new LambdaForm[methodTypes.length];
--- a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Fri Aug 19 10:03:43 2016 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Fri Aug 19 13:50:03 2016 +0200
@@ -624,6 +624,11 @@
                 return resolveFrom(name, invokerType, DelegatingMethodHandle.Holder.class);
             }
             case DELEGATE:                  return resolveFrom(name, invokerType, DelegatingMethodHandle.Holder.class);
+            case ZERO:                      // fall-through
+            case IDENTITY: {
+                name = name + "_" + form.returnType().basicTypeChar();
+                return resolveFrom(name, invokerType, LambdaForm.Holder.class);
+            }
             case DIRECT_INVOKE_INTERFACE:   // fall-through
             case DIRECT_INVOKE_SPECIAL:     // fall-through
             case DIRECT_INVOKE_STATIC:      // fall-through
--- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java	Fri Aug 19 10:03:43 2016 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java	Fri Aug 19 13:50:03 2016 +0200
@@ -270,6 +270,8 @@
 
     enum Kind {
         GENERIC(""),
+        ZERO("zero"),
+        IDENTITY("identity"),
         BOUND_REINVOKER("BMH.reinvoke"),
         REINVOKER("MH.reinvoke"),
         DELEGATE("MH.delegate"),
@@ -1848,7 +1850,7 @@
         // bootstrap dependency on this method in case we're interpreting LFs
         if (isVoid) {
             Name[] idNames = new Name[] { argument(0, L_TYPE) };
-            idForm = new LambdaForm(idMem.getName(), 1, idNames, VOID_RESULT);
+            idForm = new LambdaForm(idMem.getName(), 1, idNames, VOID_RESULT, Kind.IDENTITY);
             idForm.compileToBytecode();
             idFun = new NamedFunction(idMem, SimpleMethodHandle.make(idMem.getInvocationType(), idForm));
 
@@ -1856,13 +1858,13 @@
             zeFun = idFun;
         } else {
             Name[] idNames = new Name[] { argument(0, L_TYPE), argument(1, type) };
-            idForm = new LambdaForm(idMem.getName(), 2, idNames, 1);
+            idForm = new LambdaForm(idMem.getName(), 2, idNames, 1, Kind.IDENTITY);
             idForm.compileToBytecode();
             idFun = new NamedFunction(idMem, SimpleMethodHandle.make(idMem.getInvocationType(), idForm));
 
             Object zeValue = Wrapper.forBasicType(btChar).zero();
             Name[] zeNames = new Name[] { argument(0, L_TYPE), new Name(idFun, zeValue) };
-            zeForm = new LambdaForm(zeMem.getName(), 1, zeNames, 1);
+            zeForm = new LambdaForm(zeMem.getName(), 1, zeNames, 1, Kind.ZERO);
             zeForm.compileToBytecode();
             zeFun = new NamedFunction(zeMem, SimpleMethodHandle.make(zeMem.getInvocationType(), zeForm));
         }
@@ -1921,8 +1923,17 @@
         if (USE_PREDEFINED_INTERPRET_METHODS)
             computeInitialPreparedForms();
         NamedFunction.initializeInvokers();
+
+        // The Holder class will contain pre-generated forms resolved
+        // using MemberName.getFactory(). However, that doesn't initialize the
+        // class, which subtly breaks inlining etc. By forcing
+        // initialization of the Holder class we avoid these issues.
+        UNSAFE.ensureClassInitialized(Holder.class);
     }
 
+    /* Placeholder class for zero and identity forms generated ahead of time */
+    final class Holder {}
+
     // The following hack is necessary in order to suppress TRACE_INTERPRETER
     // during execution of the static initializes of this class.
     // Turning on TRACE_INTERPRETER too early will cause
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Fri Aug 19 10:03:43 2016 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Fri Aug 19 13:50:03 2016 +0200
@@ -1739,6 +1739,12 @@
                 return GenerateJLIClassesHelper
                         .generateConcreteBMHClassBytes(types);
             }
+
+            @Override
+            public byte[] generateBasicFormsClassBytes(final String className) {
+                return GenerateJLIClassesHelper
+                        .generateBasicFormsClassBytes(className);
+            }
         });
     }
 
--- a/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangInvokeAccess.java	Fri Aug 19 10:03:43 2016 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangInvokeAccess.java	Fri Aug 19 13:50:03 2016 +0200
@@ -72,4 +72,10 @@
      */
     Map.Entry<String, byte[]> generateConcreteBMHClassBytes(
             final String types);
+
+    /**
+     * Returns a {@code byte[]} containing the bytecode for a class implementing
+     * the zero and identity forms of all {@code LambdaForm.BasicType}s.
+     */
+    byte[] generateBasicFormsClassBytes(final String className);
 }
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java	Fri Aug 19 10:03:43 2016 +0200
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java	Fri Aug 19 13:50:03 2016 +0200
@@ -65,6 +65,8 @@
 
     private static final String DELEGATING_METHOD_HANDLE = "java/lang/invoke/DelegatingMethodHandle$Holder";
 
+    private static final String BASIC_FORMS_HANDLE = "java/lang/invoke/LambdaForm$Holder";
+
     private static final JavaLangInvokeAccess JLIA
             = SharedSecrets.getJavaLangInvokeAccess();
 
@@ -227,14 +229,15 @@
         in.transformAndCopy(entry -> {
                 // filter out placeholder entries
                 if (entry.path().equals(DIRECT_METHOD_HANDLE_ENTRY) ||
-                    entry.path().equals(DELEGATING_METHOD_HANDLE_ENTRY)) {
+                    entry.path().equals(DELEGATING_METHOD_HANDLE_ENTRY) ||
+                    entry.path().equals(BASIC_FORMS_HANDLE_ENTRY)) {
                     return null;
                 } else {
                     return entry;
                 }
             }, out);
         speciesTypes.forEach(types -> generateBMHClass(types, out));
-        generateDMHClass(out);
+        generateHolderClasses(out);
         return out.build();
     }
 
@@ -257,7 +260,7 @@
         }
     }
 
-    private void generateDMHClass(ResourcePoolBuilder out) {
+    private void generateHolderClasses(ResourcePoolBuilder out) {
         int count = 0;
         for (List<String> entry : dmhMethods.values()) {
             count += entry.size();
@@ -284,6 +287,10 @@
                     DELEGATING_METHOD_HANDLE, methodTypes);
             ndata = ResourcePoolEntry.create(DELEGATING_METHOD_HANDLE_ENTRY, bytes);
             out.add(ndata);
+
+            bytes = JLIA.generateBasicFormsClassBytes(BASIC_FORMS_HANDLE);
+            ndata = ResourcePoolEntry.create(BASIC_FORMS_HANDLE_ENTRY, bytes);
+            out.add(ndata);
         } catch (Exception ex) {
             throw new PluginException(ex);
         }
@@ -292,6 +299,8 @@
             "/java.base/" + DIRECT_METHOD_HANDLE + ".class";
     private static final String DELEGATING_METHOD_HANDLE_ENTRY =
             "/java.base/" + DELEGATING_METHOD_HANDLE + ".class";
+    private static final String BASIC_FORMS_HANDLE_ENTRY =
+            "/java.base/" + BASIC_FORMS_HANDLE + ".class";
 
     // Convert LL -> LL, L3 -> LLL
     private static String expandSignature(String signature) {