8164451: Generate all zero and identity forms at link time
Reviewed-by: shade, mhaupt, vlivanov
--- 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) {