--- old/src/java.base/share/classes/java/lang/invoke/DelegatingMethodHandle.java 2015-01-16 19:37:26.000000000 +0300 +++ new/src/java.base/share/classes/java/lang/invoke/DelegatingMethodHandle.java 2015-01-16 19:37:26.000000000 +0300 @@ -145,7 +145,7 @@ targetArgs[0] = names[NEXT_MH]; // overwrite this MH with next MH names[REINVOKE] = new LambdaForm.Name(mtype, targetArgs); } - form = new LambdaForm(debugString, ARG_LIMIT, names, forceInline); + form = new LambdaForm(debugString, ARG_LIMIT, names, forceInline, /*isGWT=*/false); if (!customized) { form = mtype.form().setCachedLambdaForm(whichCache, form); } --- old/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java 2015-01-16 19:37:28.000000000 +0300 +++ new/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java 2015-01-16 19:37:27.000000000 +0300 @@ -628,6 +628,10 @@ // Mark this method as a compiled LambdaForm mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Compiled;", true); + if (PROFILE_GWT && lambdaForm.isGWT) { + mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$IgnoreProfile;", true); + } + if (lambdaForm.forceInline) { // Force inlining of this invoker method. mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true); @@ -635,7 +639,6 @@ 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 Name onStack = null; @@ -1005,6 +1008,11 @@ // load test result emitPushArgument(selectAlternativeName, 0); + if (PROFILE_GWT) { + emitPushArgument(selectAlternativeName, 3); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, MHI, "profileBranch", "(Z[I)Z", false); + } + // if_icmpne L_fallback mv.visitJumpInsn(Opcodes.IFEQ, L_fallback); --- old/src/java.base/share/classes/java/lang/invoke/LambdaForm.java 2015-01-16 19:37:29.000000000 +0300 +++ new/src/java.base/share/classes/java/lang/invoke/LambdaForm.java 2015-01-16 19:37:29.000000000 +0300 @@ -120,6 +120,7 @@ final int arity; final int result; final boolean forceInline; + final boolean isGWT; @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, true); + this(debugName, arity, names, result, /*forceInline=*/true, /*isGWT=*/false); } LambdaForm(String debugName, - int arity, Name[] names, int result, boolean forceInline) { + int arity, Name[] names, int result, boolean forceInline, boolean isGWT) { assert(namesOK(arity, names)); this.arity = arity; this.result = fixResult(result, names); this.names = names.clone(); this.debugName = fixDebugName(debugName); this.forceInline = forceInline; + this.isGWT = isGWT; int maxOutArity = normalize(); if (maxOutArity > MethodType.MAX_MH_INVOKER_ARITY) { // Cannot use LF interpreter on very high arity expressions. @@ -263,21 +265,25 @@ } LambdaForm(String debugName, int arity, Name[] names) { - this(debugName, arity, names, LAST_RESULT, true); + this(debugName, arity, names, LAST_RESULT, /*forceInline=*/true, /*isGWT=*/false); } LambdaForm(String debugName, - int arity, Name[] names, boolean forceInline) { - this(debugName, arity, names, LAST_RESULT, forceInline); + int arity, Name[] names, boolean forceInline, boolean isGWT) { + this(debugName, arity, names, LAST_RESULT, forceInline, isGWT); } LambdaForm(String debugName, Name[] formals, Name[] temps, Name result) { this(debugName, - formals.length, buildNames(formals, temps, result), LAST_RESULT, true); + formals.length, buildNames(formals, temps, result), LAST_RESULT, /*forceInline=*/true, /*isGWT=*/false); } 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, /*isGWT=*/false); + } + + static LambdaForm makeGuardWithTestForm(int paramCount, Name[] names) { + return new LambdaForm("guard", paramCount, names, /*forceInline=*/true, /*isGWT=*/true); } private static Name[] buildNames(Name[] formals, Name[] temps, Name result) { @@ -291,10 +297,6 @@ } 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. @@ -303,7 +305,8 @@ this.result = (signatureReturn(sig) == V_TYPE ? -1 : arity); this.names = buildEmptyNames(arity, sig); this.debugName = "LF.zero"; - this.forceInline = forceInline; + this.forceInline = true; + this.isGWT = false; assert(nameRefsAreLegal()); assert(isEmpty()); assert(sig.equals(basicTypeSignature())) : sig + " != " + basicTypeSignature(); @@ -1795,6 +1798,15 @@ @interface Hidden { } + /** + * Internal marker which signals JIT that gathered profile is useless. + */ + /*non-public*/ + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + @interface IgnoreProfile { + } + private static final HashMap DEBUG_NAME_COUNTERS; static { if (debugEnabled()) --- old/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java 2015-01-16 19:37:31.000000000 +0300 +++ new/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java 2015-01-16 19:37:31.000000000 +0300 @@ -632,7 +632,7 @@ MH_selectAlternative = makeIntrinsic( IMPL_LOOKUP.findStatic(MHI, "selectAlternative", - MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class)), + MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class, int[].class)), Intrinsic.SELECT_ALTERNATIVE); } catch (ReflectiveOperationException ex) { throw newInternalError(ex); @@ -696,8 +696,35 @@ @LambdaForm.Hidden static - MethodHandle selectAlternative(boolean testResult, MethodHandle target, MethodHandle fallback) { - return testResult ? target : fallback; + MethodHandle selectAlternative(boolean testResult, MethodHandle target, MethodHandle fallback, int[] counters) { + if (counters != null) { + updateCounters(counters, testResult); + } + if (testResult) { + return target; + } else { + return fallback; + } + } + + // Intrinsified by C2. Counters are used during parsing to calculate branch frequencies. + @LambdaForm.Hidden + static + boolean profileBranch(boolean result, int[] counters) { + updateCounters(counters, result); + return result; + } + + @LambdaForm.Hidden + @ForceInline + static + void updateCounters(int[] counters, boolean result) { + int idx = result ? 0 : 1; + try { + counters[idx] = Math.addExact(counters[idx], 1); + } catch (ArithmeticException e) { + // skip update when the counter overflows + } } static @@ -707,14 +734,15 @@ MethodType type = target.type(); assert(test.type().equals(type.changeReturnType(boolean.class)) && fallback.type().equals(type)); MethodType basicType = type.basicType(); + int[] counters = PROFILE_GWT ? new int[2] : null; LambdaForm form = makeGuardWithTestForm(basicType); - BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLL(); + BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLL(); BoundMethodHandle mh; try { mh = (BoundMethodHandle) data.constructor().invokeBasic(type, form, - (Object) test, (Object) profile(target), (Object) profile(fallback)); + (Object) test, (Object) profile(target), (Object) profile(fallback), counters); } catch (Throwable ex) { throw uncaughtException(ex); } @@ -856,6 +884,7 @@ final int GET_TEST = nameCursor++; final int GET_TARGET = nameCursor++; final int GET_FALLBACK = nameCursor++; + final int GET_COUNTERS = nameCursor++; final int CALL_TEST = nameCursor++; final int SELECT_ALT = nameCursor++; final int CALL_TARGET = nameCursor++; @@ -864,12 +893,12 @@ MethodType lambdaType = basicType.invokerType(); Name[] names = arguments(nameCursor - ARG_LIMIT, lambdaType); - BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLL(); + BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLL(); names[THIS_MH] = names[THIS_MH].withConstraint(data); names[GET_TEST] = new Name(data.getterFunction(0), names[THIS_MH]); names[GET_TARGET] = new Name(data.getterFunction(1), names[THIS_MH]); names[GET_FALLBACK] = new Name(data.getterFunction(2), names[THIS_MH]); - + names[GET_COUNTERS] = new Name(data.getterFunction(3), names[THIS_MH]); Object[] invokeArgs = Arrays.copyOfRange(names, 0, ARG_LIMIT, Object[].class); // call test @@ -879,13 +908,13 @@ // call selectAlternative names[SELECT_ALT] = new Name(Lazy.MH_selectAlternative, names[CALL_TEST], - names[GET_TARGET], names[GET_FALLBACK]); + names[GET_TARGET], names[GET_FALLBACK], names[GET_COUNTERS]); // call target or fallback invokeArgs[0] = names[SELECT_ALT]; names[CALL_TARGET] = new Name(basicType, invokeArgs); - lform = new LambdaForm("guard", lambdaType.parameterCount(), names); + lform = LambdaForm.makeGuardWithTestForm(lambdaType.parameterCount(), names); return basicType.form().setCachedLambdaForm(MethodTypeForm.LF_GWT, lform); } --- old/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java 2015-01-16 19:37:33.000000000 +0300 +++ new/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java 2015-01-16 19:37:32.000000000 +0300 @@ -48,9 +48,10 @@ static final int COMPILE_THRESHOLD; static final int DONT_INLINE_THRESHOLD; static final int PROFILE_LEVEL; + static final boolean PROFILE_GWT; static { - final Object[] values = new Object[7]; + final Object[] values = new Object[8]; AccessController.doPrivileged(new PrivilegedAction() { public Void run() { values[0] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DEBUG_NAMES"); @@ -60,6 +61,7 @@ values[4] = Integer.getInteger("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD", 0); 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")); return null; } }); @@ -70,6 +72,7 @@ COMPILE_THRESHOLD = (Integer) values[4]; DONT_INLINE_THRESHOLD = (Integer) values[5]; PROFILE_LEVEL = (Integer) values[6]; + PROFILE_GWT = (Boolean) values[7]; } /** Tell if any of the debugging switches are turned on.