--- old/src/java.base/share/classes/java/lang/invoke/DelegatingMethodHandle.java 2014-10-14 22:50:02.000000000 +0400 +++ new/src/java.base/share/classes/java/lang/invoke/DelegatingMethodHandle.java 2014-10-14 22:50:01.000000000 +0400 @@ -44,6 +44,10 @@ super(type, chooseDelegatingForm(target)); } + protected DelegatingMethodHandle(MethodType type, LambdaForm form) { + super(type, form); + } + /** Define this to extract the delegated target which supplies the invocation behavior. */ abstract protected MethodHandle getTarget(); @@ -88,14 +92,31 @@ return makeReinvokerForm(target, MethodTypeForm.LF_DELEGATE, DelegatingMethodHandle.class, NF_getTarget); } - /** Create a LF which simply reinvokes a target of the given basic type. */ static LambdaForm makeReinvokerForm(MethodHandle target, int whichCache, Object constraint, NamedFunction getTargetFn) { + String debugString; + switch(whichCache) { + case MethodTypeForm.LF_REBIND: debugString = "BMH.reinvoke"; break; + case MethodTypeForm.LF_DELEGATE: debugString = "MH.delegate"; break; + default: debugString = "MH.reinvoke"; break; + } + // No pre-action needed. + return makeReinvokerForm(target, whichCache, constraint, debugString, true, getTargetFn, null); + } + /** Create a LF which simply reinvokes a target of the given basic type. */ + static LambdaForm makeReinvokerForm(MethodHandle target, + int whichCache, + Object constraint, + String debugString, + boolean forceInline, + NamedFunction getTargetFn, + NamedFunction preActionFn) { MethodType mtype = target.type().basicType(); boolean customized = (whichCache < 0 || mtype.parameterSlotCount() > MethodType.MAX_MH_INVOKER_ARITY); + boolean hasPreAction = (preActionFn != null); LambdaForm form; if (!customized) { form = mtype.form().cachedLambdaForm(whichCache); @@ -105,12 +126,16 @@ final int ARG_BASE = 1; final int ARG_LIMIT = ARG_BASE + mtype.parameterCount(); int nameCursor = ARG_LIMIT; + final int PRE_ACTION = hasPreAction ? nameCursor++ : -1; final int NEXT_MH = customized ? -1 : nameCursor++; final int REINVOKE = nameCursor++; LambdaForm.Name[] names = LambdaForm.arguments(nameCursor - ARG_LIMIT, mtype.invokerType()); assert(names.length == nameCursor); names[THIS_DMH] = names[THIS_DMH].withConstraint(constraint); Object[] targetArgs; + if (hasPreAction) { + names[PRE_ACTION] = new LambdaForm.Name(preActionFn, names[THIS_DMH]); + } if (customized) { targetArgs = Arrays.copyOfRange(names, ARG_BASE, ARG_LIMIT, Object[].class); names[REINVOKE] = new LambdaForm.Name(target, targetArgs); // the invoker is the target itself @@ -120,20 +145,14 @@ targetArgs[0] = names[NEXT_MH]; // overwrite this MH with next MH names[REINVOKE] = new LambdaForm.Name(mtype, targetArgs); } - String debugString; - switch(whichCache) { - case MethodTypeForm.LF_REBIND: debugString = "BMH.reinvoke"; break; - case MethodTypeForm.LF_DELEGATE: debugString = "MH.delegate"; break; - default: debugString = "MH.reinvoke"; break; - } - form = new LambdaForm(debugString, ARG_LIMIT, names); + form = new LambdaForm(debugString, ARG_LIMIT, names, forceInline); if (!customized) { form = mtype.form().setCachedLambdaForm(whichCache, form); } return form; } - private static final NamedFunction NF_getTarget; + static final NamedFunction NF_getTarget; static { try { NF_getTarget = new NamedFunction(DelegatingMethodHandle.class --- old/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java 2014-10-14 22:50:03.000000000 +0400 +++ new/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java 2014-10-14 22:50:02.000000000 +0400 @@ -628,8 +628,13 @@ // Mark this method as a compiled LambdaForm mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Compiled;", true); - // Force inlining of this invoker method. - mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true); + if (lambdaForm.forceInline) { + // Force inlining of this invoker method. + mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true); + } else { + mv.visitAnnotation("Ljava/lang/invoke/DontInline;", true); + } + // iterate over the form's names, generating bytecode instructions for each // start iterating at the first name following the arguments --- old/src/java.base/share/classes/java/lang/invoke/LambdaForm.java 2014-10-14 22:50:04.000000000 +0400 +++ new/src/java.base/share/classes/java/lang/invoke/LambdaForm.java 2014-10-14 22:50:04.000000000 +0400 @@ -119,6 +119,7 @@ class LambdaForm { final int arity; final int result; + final boolean forceInline; @Stable final Name[] names; final String debugName; MemberName vmentry; // low-level behavior, or null if not yet prepared @@ -243,11 +244,16 @@ LambdaForm(String debugName, int arity, Name[] names, int result) { + this(debugName, arity, names, result, true); + } + LambdaForm(String debugName, + int arity, Name[] names, int result, boolean forceInline) { assert(namesOK(arity, names)); this.arity = arity; this.result = fixResult(result, names); this.names = names.clone(); this.debugName = fixDebugName(debugName); + this.forceInline = forceInline; int maxOutArity = normalize(); if (maxOutArity > MethodType.MAX_MH_INVOKER_ARITY) { // Cannot use LF interpreter on very high arity expressions. @@ -255,17 +261,23 @@ compileToBytecode(); } } - LambdaForm(String debugName, int arity, Name[] names) { - this(debugName, - arity, names, LAST_RESULT); + this(debugName, arity, names, LAST_RESULT, true); + } + LambdaForm(String debugName, + int arity, Name[] names, boolean forceInline) { + this(debugName, arity, names, LAST_RESULT, forceInline); } - LambdaForm(String debugName, Name[] formals, Name[] temps, Name result) { this(debugName, - formals.length, buildNames(formals, temps, result), LAST_RESULT); + formals.length, buildNames(formals, temps, result), LAST_RESULT, true); + } + LambdaForm(String debugName, + Name[] formals, Name[] temps, Name result, boolean forceInline) { + this(debugName, + formals.length, buildNames(formals, temps, result), LAST_RESULT, forceInline); } private static Name[] buildNames(Name[] formals, Name[] temps, Name result) { @@ -279,6 +291,10 @@ } private LambdaForm(String sig) { + this(sig, true); + } + + private LambdaForm(String sig, boolean forceInline) { // Make a blank lambda form, which returns a constant zero or null. // It is used as a template for managing the invocation of similar forms that are non-empty. // Called only from getPreparedForm. @@ -287,6 +303,7 @@ this.result = (signatureReturn(sig) == V_TYPE ? -1 : arity); this.names = buildEmptyNames(arity, sig); this.debugName = "LF.zero"; + this.forceInline = forceInline; assert(nameRefsAreLegal()); assert(isEmpty()); assert(sig.equals(basicTypeSignature())) : sig + " != " + basicTypeSignature(); --- old/src/java.base/share/classes/java/lang/invoke/MethodHandle.java 2014-10-14 22:50:05.000000000 +0400 +++ new/src/java.base/share/classes/java/lang/invoke/MethodHandle.java 2014-10-14 22:50:05.000000000 +0400 @@ -1438,10 +1438,9 @@ /*non-public*/ void updateForm(LambdaForm newForm) { if (form == newForm) return; - assert(this instanceof DirectMethodHandle && this.internalMemberName().isStatic()); - // ISSUE: Should we have a memory fence here? + newForm.prepare(); // as in MethodHandle. UNSAFE.putObject(this, FORM_OFFSET, newForm); - this.form.prepare(); // as in MethodHandle. + UNSAFE.fullFence(); } private static final long FORM_OFFSET; --- old/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java 2014-10-14 22:50:06.000000000 +0400 +++ new/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java 2014-10-14 22:50:06.000000000 +0400 @@ -707,10 +707,11 @@ LambdaForm form = makeGuardWithTestForm(basicType); BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLL(); BoundMethodHandle mh; + try { mh = (BoundMethodHandle) data.constructor().invokeBasic(type, form, - (Object) test, (Object) target, (Object) fallback); + (Object) test, (Object) profile(target), (Object) profile(fallback)); } catch (Throwable ex) { throw uncaughtException(ex); } @@ -718,6 +719,97 @@ return mh; } + + static + MethodHandle profile(MethodHandle target) { + if (DONT_INLINE_THRESHOLD >= 0) { + return BlockInliningWrapper.make(target); + } else { + return target; + } + } + + /** + * Block inlining during JIT-compilation of a target method handle if it hasn't been invoked enough times. + * Corresponding LambdaForm has @DontInline when compiled into bytecode. + */ + static class BlockInliningWrapper extends DelegatingMethodHandle { + private final MethodHandle target; + private int count; + private volatile boolean isUnblocked; + + private BlockInliningWrapper(MethodHandle target, LambdaForm lform, int count) { + super(target.type(), lform); + this.target = target; + this.count = count; + this.isUnblocked = (count > 0); + } + + @Hidden + @Override + protected MethodHandle getTarget() { + return target; + } + + @Override + public MethodHandle asTypeUncached(MethodType newType) { + MethodHandle newTarget = target.asType(newType); + return asTypeCache = isUnblocked ? make(newTarget) + : newTarget; // no need for a wrapper anymore + } + + boolean unblock() { + if (count <= 0) { + // Try to limit number of updates. MethodHandle.updateForm() doesn't guarantee LF update visibility. + if (isUnblocked) { + isUnblocked = false; + return true; + } else { + return false; + } + } else { + --count; + return false; + } + } + + static MethodHandle make(MethodHandle target) { + LambdaForm lform; + if (DONT_INLINE_THRESHOLD > 0) { + lform = DelegatingMethodHandle.makeReinvokerForm(target, + MethodTypeForm.LF_DELEGATE_BLOCK_INLINING, BlockInliningWrapper.class, "reinvoker.dontInline", false, + DelegatingMethodHandle.NF_getTarget, NF_maybeUnblock); + } else { + lform = DelegatingMethodHandle.makeReinvokerForm( + target, MethodTypeForm.LF_DELEGATE, DelegatingMethodHandle.class, NF_getTarget); + } + MethodHandle wrapper = new BlockInliningWrapper(target, lform, DONT_INLINE_THRESHOLD); + return wrapper; + } + + @Hidden + static void maybeUnblock(Object o1) { + BlockInliningWrapper wrapper = (BlockInliningWrapper) o1; + if (wrapper.unblock()) { + // Reached invocation threshold. Replace counting/blocking wrapper with a reinvoker. + MethodHandle target = wrapper.getTarget(); + LambdaForm lform = DelegatingMethodHandle.makeReinvokerForm( + target, MethodTypeForm.LF_DELEGATE, DelegatingMethodHandle.class, NF_getTarget); + wrapper.updateForm(lform); + } + } + + static final NamedFunction NF_maybeUnblock; + static { + Class THIS_CLASS = BlockInliningWrapper.class; + try { + NF_maybeUnblock = new NamedFunction(THIS_CLASS.getDeclaredMethod("maybeUnblock", Object.class)); + } catch (ReflectiveOperationException ex) { + throw newInternalError(ex); + } + } + } + static LambdaForm makeGuardWithTestForm(MethodType basicType) { LambdaForm lform = basicType.form().cachedLambdaForm(MethodTypeForm.LF_GWT); --- old/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java 2014-10-14 22:50:07.000000000 +0400 +++ new/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java 2014-10-14 22:50:07.000000000 +0400 @@ -47,10 +47,11 @@ static final boolean TRACE_METHOD_LINKAGE; static final boolean USE_LAMBDA_FORM_EDITOR; static final int COMPILE_THRESHOLD; + static final int DONT_INLINE_THRESHOLD; static final int PROFILE_LEVEL; static { - final Object[] values = { false, false, false, false, false, null, null }; + final Object[] values = new Object[8]; AccessController.doPrivileged(new PrivilegedAction() { public Void run() { values[0] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DEBUG_NAMES"); @@ -59,7 +60,8 @@ values[3] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE"); values[4] = Boolean.getBoolean("java.lang.invoke.MethodHandle.USE_LF_EDITOR"); values[5] = Integer.getInteger("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD", 30); - values[6] = Integer.getInteger("java.lang.invoke.MethodHandle.PROFILE_LEVEL", 0); + values[6] = Integer.getInteger("java.lang.invoke.MethodHandle.DONT_INLINE_THRESHOLD", 0); + values[7] = Integer.getInteger("java.lang.invoke.MethodHandle.PROFILE_LEVEL", 0); return null; } }); @@ -69,7 +71,8 @@ TRACE_METHOD_LINKAGE = (Boolean) values[3]; USE_LAMBDA_FORM_EDITOR = (Boolean) values[4]; COMPILE_THRESHOLD = (Integer) values[5]; - PROFILE_LEVEL = (Integer) values[6]; + DONT_INLINE_THRESHOLD = (Integer) values[6]; + PROFILE_LEVEL = (Integer) values[7]; } /** Tell if any of the debugging switches are turned on. --- old/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java 2014-10-14 22:50:08.000000000 +0400 +++ new/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java 2014-10-14 22:50:08.000000000 +0400 @@ -63,24 +63,25 @@ final @Stable LambdaForm[] lambdaForms; // Indexes into lambdaForms: static final int - LF_INVVIRTUAL = 0, // DMH invokeVirtual - LF_INVSTATIC = 1, - LF_INVSPECIAL = 2, - LF_NEWINVSPECIAL = 3, - LF_INVINTERFACE = 4, - LF_INVSTATIC_INIT = 5, // DMH invokeStatic with barrier - LF_INTERPRET = 6, // LF interpreter - LF_REBIND = 7, // BoundMethodHandle - LF_DELEGATE = 8, // DelegatingMethodHandle - LF_EX_LINKER = 9, // invokeExact_MT (for invokehandle) - LF_EX_INVOKER = 10, // MHs.invokeExact - LF_GEN_LINKER = 11, // generic invoke_MT (for invokehandle) - LF_GEN_INVOKER = 12, // generic MHs.invoke - LF_CS_LINKER = 13, // linkToCallSite_CS - LF_MH_LINKER = 14, // linkToCallSite_MH - LF_GWC = 15, // guardWithCatch (catchException) - LF_GWT = 16, // guardWithTest - LF_LIMIT = 17; + LF_INVVIRTUAL = 0, // DMH invokeVirtual + LF_INVSTATIC = 1, + LF_INVSPECIAL = 2, + LF_NEWINVSPECIAL = 3, + LF_INVINTERFACE = 4, + LF_INVSTATIC_INIT = 5, // DMH invokeStatic with barrier + LF_INTERPRET = 6, // LF interpreter + LF_REBIND = 7, // BoundMethodHandle + LF_DELEGATE = 8, // DelegatingMethodHandle + LF_DELEGATE_BLOCK_INLINING = 9, // Counting DelegatingMethodHandle w/ @DontInline + LF_EX_LINKER = 10, // invokeExact_MT (for invokehandle) + LF_EX_INVOKER = 11, // MHs.invokeExact + LF_GEN_LINKER = 12, // generic invoke_MT (for invokehandle) + LF_GEN_INVOKER = 13, // generic MHs.invoke + LF_CS_LINKER = 14, // linkToCallSite_CS + LF_MH_LINKER = 15, // linkToCallSite_MH + LF_GWC = 16, // guardWithCatch (catchException) + LF_GWT = 17, // guardWithTest + LF_LIMIT = 18; /** Return the type corresponding uniquely (1-1) to this MT-form. * It might have any primitive returns or arguments, but will have no references except Object.