--- old/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java 2015-01-21 19:19:56.000000000 +0300 +++ new/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java 2015-01-21 19:19:56.000000000 +0300 @@ -56,9 +56,11 @@ private static final String OBJ = "java/lang/Object"; private static final String OBJARY = "[Ljava/lang/Object;"; + private static final String MH_SIG = "L" + MH + ";"; private static final String LF_SIG = "L" + LF + ";"; private static final String LFN_SIG = "L" + LFN + ";"; private static final String LL_SIG = "(L" + OBJ + ";)L" + OBJ + ";"; + private static final String LLV_SIG = "(L" + OBJ + ";L" + OBJ + ";)V"; private static final String CLL_SIG = "(L" + CLS + ";L" + OBJ + ";)L" + OBJ + ";"; /** Name of its super class*/ @@ -627,6 +629,15 @@ return false; } + /** Generates code to check that actual receiver and LambdaForm matches */ + private boolean checkActualReceiver() { + // Expects MethodHandle on the stack and actual receiver MethodHandle in slot #0 + mv.visitInsn(Opcodes.DUP); + mv.visitVarInsn(Opcodes.ALOAD, localsMap[0]); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, MHI, "assertSame", LLV_SIG, false); + return true; + } + /** * Generate an invoker method for the passed {@link LambdaForm}. */ @@ -652,6 +663,17 @@ mv.visitAnnotation("Ljava/lang/invoke/DontInline;", true); } + if (lambdaForm.customized != null) { + // Since LambdaForm is customized for a particular MethodHandle, it's safe to substitute + // receiver MethodHandle (at slot #0) with an embedded constant and use it instead. + // It enables more efficient code generation in some situations, since embedded constants + // are compile-time constants for JIT compiler. + mv.visitLdcInsn(constantPlaceholder(lambdaForm.customized)); + mv.visitTypeInsn(Opcodes.CHECKCAST, MH); + assert(checkActualReceiver()); // expects MethodHandle on top of the stack + mv.visitVarInsn(Opcodes.ASTORE, localsMap[0]); + } + // iterate over the form's names, generating bytecode instructions for each // start iterating at the first name following the arguments Name onStack = null; --- old/src/java.base/share/classes/java/lang/invoke/Invokers.java 2015-01-21 19:19:58.000000000 +0300 +++ new/src/java.base/share/classes/java/lang/invoke/Invokers.java 2015-01-21 19:19:58.000000000 +0300 @@ -247,6 +247,7 @@ int nameCursor = OUTARG_LIMIT; final int MTYPE_ARG = customized ? -1 : nameCursor++; // might be last in-argument final int CHECK_TYPE = nameCursor++; + final int CHECK_CUSTOM = (CUSTOMIZE_THRESHOLD >= 0) ? nameCursor++ : -1; final int LINKER_CALL = nameCursor++; MethodType invokerFormType = mtype.invokerType(); if (isLinker) { @@ -279,6 +280,9 @@ // mh.invokeGeneric(a*):R => checkGenericType(mh, TYPEOF(a*:R)).invokeBasic(a*) outArgs[0] = names[CHECK_TYPE]; } + if (CHECK_CUSTOM != -1) { + names[CHECK_CUSTOM] = new Name(NF_checkCustomized, names[CALL_MH]); + } names[LINKER_CALL] = new Name(outCallType, outArgs); lform = new LambdaForm(debugName, INARG_LIMIT, names); if (isLinker) @@ -386,11 +390,30 @@ return ((CallSite)site).getTarget(); } + /*non-public*/ static + @ForceInline + void checkCustomized(MethodHandle mh) { + if (mh.form.customized == null) { + maybeCustomize(mh); + } + } + + /*non-public*/ static + @DontInline + void maybeCustomize(MethodHandle mh) { + if (mh.customizationCount >= CUSTOMIZE_THRESHOLD) { + mh.customize(); + } else { + mh.customizationCount += 1; + } + } + // Local constant functions: private static final NamedFunction NF_checkExactType, NF_checkGenericType, - NF_getCallSiteTarget; + NF_getCallSiteTarget, + NF_checkCustomized; static { try { NamedFunction nfs[] = { @@ -399,7 +422,9 @@ NF_checkGenericType = new NamedFunction(Invokers.class .getDeclaredMethod("checkGenericType", Object.class, Object.class)), NF_getCallSiteTarget = new NamedFunction(Invokers.class - .getDeclaredMethod("getCallSiteTarget", Object.class)) + .getDeclaredMethod("getCallSiteTarget", Object.class)), + NF_checkCustomized = new NamedFunction(Invokers.class + .getDeclaredMethod("checkCustomized", MethodHandle.class)) }; for (NamedFunction nf : nfs) { // Each nf must be statically invocable or we get tied up in our bootstraps. --- old/src/java.base/share/classes/java/lang/invoke/LambdaForm.java 2015-01-21 19:20:00.000000000 +0300 +++ new/src/java.base/share/classes/java/lang/invoke/LambdaForm.java 2015-01-21 19:20:00.000000000 +0300 @@ -120,6 +120,7 @@ final int arity; final int result; final boolean forceInline; + final MethodHandle customized; @Stable final Name[] names; final String debugName; MemberName vmentry; // low-level behavior, or null if not yet prepared @@ -244,16 +245,17 @@ LambdaForm(String debugName, int arity, Name[] names, int result) { - this(debugName, arity, names, result, /*forceInline=*/true); + this(debugName, arity, names, result, /*forceInline=*/true, /*customized=*/null); } LambdaForm(String debugName, - int arity, Name[] names, int result, boolean forceInline) { + int arity, Name[] names, int result, boolean forceInline, MethodHandle customized) { assert(namesOK(arity, names)); this.arity = arity; this.result = fixResult(result, names); this.names = names.clone(); this.debugName = fixDebugName(debugName); this.forceInline = forceInline; + this.customized = customized; int maxOutArity = normalize(); if (maxOutArity > MethodType.MAX_MH_INVOKER_ARITY) { // Cannot use LF interpreter on very high arity expressions. @@ -263,21 +265,21 @@ } LambdaForm(String debugName, int arity, Name[] names) { - this(debugName, arity, names, LAST_RESULT, /*forceInline=*/true); + this(debugName, arity, names, LAST_RESULT, /*forceInline=*/true, /*customized=*/null); } LambdaForm(String debugName, int arity, Name[] names, boolean forceInline) { - this(debugName, arity, names, LAST_RESULT, forceInline); + this(debugName, arity, names, LAST_RESULT, forceInline, /*customized=*/null); } LambdaForm(String debugName, Name[] formals, Name[] temps, Name result) { this(debugName, - formals.length, buildNames(formals, temps, result), LAST_RESULT, /*forceInline=*/true); + formals.length, buildNames(formals, temps, result), LAST_RESULT, /*forceInline=*/true, /*customized=*/null); } LambdaForm(String debugName, Name[] formals, Name[] temps, Name result, boolean forceInline) { this(debugName, - formals.length, buildNames(formals, temps, result), LAST_RESULT, forceInline); + formals.length, buildNames(formals, temps, result), LAST_RESULT, forceInline, /*customized=*/null); } private static Name[] buildNames(Name[] formals, Name[] temps, Name result) { @@ -300,6 +302,7 @@ this.names = buildEmptyNames(arity, sig); this.debugName = "LF.zero"; this.forceInline = true; + this.customized = null; assert(nameRefsAreLegal()); assert(isEmpty()); assert(sig.equals(basicTypeSignature())) : sig + " != " + basicTypeSignature(); @@ -413,8 +416,8 @@ for (int i = arity; i < names.length; i++) { names[i].internArguments(); } - assert(nameRefsAreLegal()); } + assert(nameRefsAreLegal()); return maxOutArity; } --- old/src/java.base/share/classes/java/lang/invoke/MethodHandle.java 2015-01-21 19:20:02.000000000 +0300 +++ new/src/java.base/share/classes/java/lang/invoke/MethodHandle.java 2015-01-21 19:20:02.000000000 +0300 @@ -434,6 +434,8 @@ // form is not private so that invokers can easily fetch it /*private*/ MethodHandle asTypeCache; // asTypeCache is not private so that invokers can easily fetch it + /*non-public*/ byte customizationCount; + // customizationCount should be accessible from invokers /** * Reports the type of this method handle. @@ -1431,6 +1433,17 @@ UNSAFE.fullFence(); } + /** Craft a LambdaForm customized for this particular MethodHandle */ + /*non-public*/ + void customize() { + if (form.customized == null) { + LambdaForm newForm = new LambdaForm(form.debugName, form.arity, form.names, form.result, form.forceInline, this); + updateForm(newForm); + } else { + assert(form.customized == this); + } + } + private static final long FORM_OFFSET; static { try { --- old/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java 2015-01-21 19:20:04.000000000 +0300 +++ new/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java 2015-01-21 19:20:04.000000000 +0300 @@ -1664,4 +1664,13 @@ assert(elemType.isPrimitive()); return Lazy.MH_copyAsPrimitiveArray.bindTo(Wrapper.forPrimitiveType(elemType)); } + + /*non-public*/ static void assertSame(Object mh1, Object mh2) { + if (mh1 != mh2) { + String msg = String.format("mh1 != mh2: mh1 = %s (form: %s); mh2 = %s (form: %s)", + mh1, ((MethodHandle)mh1).form, + mh2, ((MethodHandle)mh2).form); + throw newInternalError(msg); + } + } } --- old/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java 2015-01-21 19:20:06.000000000 +0300 +++ new/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java 2015-01-21 19:20:06.000000000 +0300 @@ -49,9 +49,10 @@ static final int DONT_INLINE_THRESHOLD; static final int PROFILE_LEVEL; static final boolean PROFILE_GWT; + static final int CUSTOMIZE_THRESHOLD; static { - final Object[] values = new Object[8]; + final Object[] values = new Object[9]; AccessController.doPrivileged(new PrivilegedAction() { public Void run() { values[0] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DEBUG_NAMES"); @@ -62,6 +63,7 @@ values[5] = Integer.getInteger("java.lang.invoke.MethodHandle.DONT_INLINE_THRESHOLD", 30); values[6] = Integer.getInteger("java.lang.invoke.MethodHandle.PROFILE_LEVEL", 0); values[7] = Boolean.parseBoolean(System.getProperty("java.lang.invoke.MethodHandle.PROFILE_GWT", "true")); + values[8] = Integer.getInteger("java.lang.invoke.MethodHandle.CUSTOMIZE_THRESHOLD", 127); return null; } }); @@ -73,6 +75,7 @@ DONT_INLINE_THRESHOLD = (Integer) values[5]; PROFILE_LEVEL = (Integer) values[6]; PROFILE_GWT = (Boolean) values[7]; + CUSTOMIZE_THRESHOLD = (Integer) values[8]; } /** Tell if any of the debugging switches are turned on.