# HG changeset patch # User vlivanov # Date 1410362388 -14400 # Node ID 2d57604f9299cef244409bc82accab8a2a5ebd3c # Parent d69abed3a07d23895a9620eb5981eecfc3ace8a7 8050053: Improve caching of different invokers Reviewed-by: vlivanov, psandoz Contributed-by: john.r.rose@oracle.com diff -r d69abed3a07d -r 2d57604f9299 jdk/src/java.base/share/classes/java/lang/invoke/CallSite.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/CallSite.java Wed Sep 10 19:19:47 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/CallSite.java Wed Sep 10 19:19:48 2014 +0400 @@ -102,7 +102,7 @@ */ /*package-private*/ CallSite(MethodType type) { - target = type.invokers().uninitializedCallSite(); + target = makeUninitializedCallSite(type); } /** @@ -217,21 +217,34 @@ } private static final MethodHandle GET_TARGET; + private static final MethodHandle THROW_UCS; static { try { GET_TARGET = IMPL_LOOKUP. findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class)); + THROW_UCS = IMPL_LOOKUP. + findStatic(CallSite.class, "uninitializedCallSite", MethodType.methodType(Object.class, Object[].class)); } catch (ReflectiveOperationException e) { throw newInternalError(e); } } /** This guy is rolled into the default target if a MethodType is supplied to the constructor. */ - /*package-private*/ - static Empty uninitializedCallSite() { + private static Object uninitializedCallSite(Object... ignore) { throw new IllegalStateException("uninitialized call site"); } + private MethodHandle makeUninitializedCallSite(MethodType targetType) { + MethodType basicType = targetType.basicType(); + MethodHandle invoker = basicType.form().cachedMethodHandle(MethodTypeForm.MH_UNINIT_CS); + if (invoker == null) { + invoker = THROW_UCS.asType(basicType); + invoker = basicType.form().setCachedMethodHandle(MethodTypeForm.MH_UNINIT_CS, invoker); + } + // unchecked view is OK since no values will be received or returned + return invoker.viewAsType(targetType); + } + // unsafe stuff: private static final long TARGET_OFFSET; static { diff -r d69abed3a07d -r 2d57604f9299 jdk/src/java.base/share/classes/java/lang/invoke/Invokers.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/Invokers.java Wed Sep 10 19:19:47 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/Invokers.java Wed Sep 10 19:19:48 2014 +0400 @@ -25,8 +25,9 @@ package java.lang.invoke; +import java.lang.reflect.Array; import java.util.Arrays; -import sun.invoke.empty.Empty; + import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandleNatives.Constants.*; import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; @@ -40,52 +41,63 @@ // exact type (sans leading target MH) for the outgoing call private final MethodType targetType; - // FIXME: Get rid of the invokers that are not useful. - - // exact invoker for the outgoing call - private /*lazy*/ MethodHandle exactInvoker; - private /*lazy*/ MethodHandle basicInvoker; // invokeBasic (unchecked exact) - - // erased (partially untyped but with primitives) invoker for the outgoing call - // FIXME: get rid of - private /*lazy*/ MethodHandle erasedInvoker; - // FIXME: get rid of - /*lazy*/ MethodHandle erasedInvokerWithDrops; // for InvokeGeneric - - // general invoker for the outgoing call - private /*lazy*/ MethodHandle generalInvoker; - - // general invoker for the outgoing call, uses varargs - private /*lazy*/ MethodHandle varargsInvoker; - - // general invoker for the outgoing call; accepts a trailing Object[] - private final /*lazy*/ MethodHandle[] spreadInvokers; - - // invoker for an unbound callsite - private /*lazy*/ MethodHandle uninitializedCallSite; + // Cached adapter information: + private final @Stable MethodHandle[] invokers = new MethodHandle[INV_LIMIT]; + // Indexes into invokers: + static final int + INV_EXACT = 0, // MethodHandles.exactInvoker + INV_GENERIC = 1, // MethodHandles.invoker (generic invocation) + INV_BASIC = 2, // MethodHandles.basicInvoker + INV_LIMIT = 3; /** Compute and cache information common to all collecting adapters * that implement members of the erasure-family of the given erased type. */ /*non-public*/ Invokers(MethodType targetType) { this.targetType = targetType; - this.spreadInvokers = new MethodHandle[targetType.parameterCount()+1]; } /*non-public*/ MethodHandle exactInvoker() { - MethodHandle invoker = exactInvoker; + MethodHandle invoker = cachedInvoker(INV_EXACT); if (invoker != null) return invoker; invoker = makeExactOrGeneralInvoker(true); - exactInvoker = invoker; - return invoker; + return setCachedInvoker(INV_EXACT, invoker); + } + + /*non-public*/ MethodHandle genericInvoker() { + MethodHandle invoker = cachedInvoker(INV_GENERIC); + if (invoker != null) return invoker; + invoker = makeExactOrGeneralInvoker(false); + return setCachedInvoker(INV_GENERIC, invoker); } - /*non-public*/ MethodHandle generalInvoker() { - MethodHandle invoker = generalInvoker; + /*non-public*/ MethodHandle basicInvoker() { + MethodHandle invoker = cachedInvoker(INV_BASIC); if (invoker != null) return invoker; - invoker = makeExactOrGeneralInvoker(false); - generalInvoker = invoker; - return invoker; + MethodType basicType = targetType.basicType(); + if (basicType != targetType) { + // double cache; not used significantly + return setCachedInvoker(INV_BASIC, basicType.invokers().basicInvoker()); + } + invoker = basicType.form().cachedMethodHandle(MethodTypeForm.MH_BASIC_INV); + if (invoker == null) { + MemberName method = invokeBasicMethod(basicType); + invoker = DirectMethodHandle.make(method); + assert(checkInvoker(invoker)); + invoker = basicType.form().setCachedMethodHandle(MethodTypeForm.MH_BASIC_INV, invoker); + } + return setCachedInvoker(INV_BASIC, invoker); + } + + private MethodHandle cachedInvoker(int idx) { + return invokers[idx]; + } + + private synchronized MethodHandle setCachedInvoker(int idx, final MethodHandle invoker) { + // Simulate a CAS, to avoid racy duplication of results. + MethodHandle prev = invokers[idx]; + if (prev != null) return prev; + return invokers[idx] = invoker; } private MethodHandle makeExactOrGeneralInvoker(boolean isExact) { @@ -110,21 +122,6 @@ } } - /*non-public*/ MethodHandle basicInvoker() { - MethodHandle invoker = basicInvoker; - if (invoker != null) return invoker; - MethodType basicType = targetType.basicType(); - if (basicType != targetType) { - // double cache; not used significantly - return basicInvoker = basicType.invokers().basicInvoker(); - } - MemberName method = invokeBasicMethod(basicType); - invoker = DirectMethodHandle.make(method); - assert(checkInvoker(invoker)); - basicInvoker = invoker; - return invoker; - } - // This next one is called from LambdaForm.NamedFunction.. /*non-public*/ static MemberName invokeBasicMethod(MethodType basicType) { assert(basicType == basicType.basicType()); @@ -145,87 +142,42 @@ return true; } - // FIXME: get rid of - /*non-public*/ MethodHandle erasedInvoker() { - MethodHandle xinvoker = exactInvoker(); - MethodHandle invoker = erasedInvoker; - if (invoker != null) return invoker; - MethodType erasedType = targetType.erase(); - invoker = xinvoker.asType(erasedType.invokerType()); - erasedInvoker = invoker; - return invoker; + /** + * Find or create an invoker which passes unchanged a given number of arguments + * and spreads the rest from a trailing array argument. + * The invoker target type is the post-spread type {@code (TYPEOF(uarg*), TYPEOF(sarg*))=>RT}. + * All the {@code sarg}s must have a common type {@code C}. (If there are none, {@code Object} is assumed.} + * @param leadingArgCount the number of unchanged (non-spread) arguments + * @return {@code invoker.invokeExact(mh, uarg*, C[]{sarg*}) := (RT)mh.invoke(uarg*, sarg*)} + */ + /*non-public*/ MethodHandle spreadInvoker(int leadingArgCount) { + int spreadArgCount = targetType.parameterCount() - leadingArgCount; + MethodType postSpreadType = targetType; + Class argArrayType = impliedRestargType(postSpreadType, leadingArgCount); + if (postSpreadType.parameterSlotCount() <= MethodType.MAX_MH_INVOKER_ARITY) { + return genericInvoker().asSpreader(argArrayType, spreadArgCount); + } + // Cannot build a generic invoker here of type ginvoker.invoke(mh, a*[254]). + // Instead, factor sinvoker.invoke(mh, a) into ainvoker.invoke(filter(mh), a) + // where filter(mh) == mh.asSpreader(Object[], spreadArgCount) + MethodType preSpreadType = postSpreadType + .replaceParameterTypes(leadingArgCount, postSpreadType.parameterCount(), argArrayType); + MethodHandle arrayInvoker = MethodHandles.invoker(preSpreadType); + MethodHandle makeSpreader = MethodHandles.insertArguments(Lazy.MH_asSpreader, 1, argArrayType, spreadArgCount); + return MethodHandles.filterArgument(arrayInvoker, 0, makeSpreader); } - /*non-public*/ MethodHandle spreadInvoker(int leadingArgCount) { - MethodHandle vaInvoker = spreadInvokers[leadingArgCount]; - if (vaInvoker != null) return vaInvoker; - int spreadArgCount = targetType.parameterCount() - leadingArgCount; - MethodType spreadInvokerType = targetType - .replaceParameterTypes(leadingArgCount, targetType.parameterCount(), Object[].class); - if (targetType.parameterSlotCount() <= MethodType.MAX_MH_INVOKER_ARITY) { - // Factor sinvoker.invoke(mh, a) into ginvoker.asSpreader().invoke(mh, a) - // where ginvoker.invoke(mh, a*) => mh.invoke(a*). - MethodHandle genInvoker = generalInvoker(); - vaInvoker = genInvoker.asSpreader(Object[].class, spreadArgCount); - } else { - // Cannot build a general invoker here of type ginvoker.invoke(mh, a*[254]). - // Instead, factor sinvoker.invoke(mh, a) into ainvoker.invoke(filter(mh), a) - // where filter(mh) == mh.asSpreader(Object[], spreadArgCount) - MethodHandle arrayInvoker = MethodHandles.exactInvoker(spreadInvokerType); - MethodHandle makeSpreader; - try { - makeSpreader = IMPL_LOOKUP - .findVirtual(MethodHandle.class, "asSpreader", - MethodType.methodType(MethodHandle.class, Class.class, int.class)); - } catch (ReflectiveOperationException ex) { - throw newInternalError(ex); - } - makeSpreader = MethodHandles.insertArguments(makeSpreader, 1, Object[].class, spreadArgCount); - vaInvoker = MethodHandles.filterArgument(arrayInvoker, 0, makeSpreader); + private static Class impliedRestargType(MethodType restargType, int fromPos) { + if (restargType.isGeneric()) return Object[].class; // can be nothing else + int maxPos = restargType.parameterCount(); + if (fromPos >= maxPos) return Object[].class; // reasonable default + Class argType = restargType.parameterType(fromPos); + for (int i = fromPos+1; i < maxPos; i++) { + if (argType != restargType.parameterType(i)) + throw newIllegalArgumentException("need homogeneous rest arguments", restargType); } - assert(vaInvoker.type().equals(spreadInvokerType.invokerType())); - maybeCompileToBytecode(vaInvoker); - spreadInvokers[leadingArgCount] = vaInvoker; - return vaInvoker; - } - - /*non-public*/ MethodHandle varargsInvoker() { - MethodHandle vaInvoker = varargsInvoker; - if (vaInvoker != null) return vaInvoker; - vaInvoker = spreadInvoker(0).asType(MethodType.genericMethodType(0, true).invokerType()); - varargsInvoker = vaInvoker; - return vaInvoker; - } - - private static MethodHandle THROW_UCS = null; - - /*non-public*/ MethodHandle uninitializedCallSite() { - MethodHandle invoker = uninitializedCallSite; - if (invoker != null) return invoker; - if (targetType.parameterCount() > 0) { - MethodType type0 = targetType.dropParameterTypes(0, targetType.parameterCount()); - Invokers invokers0 = type0.invokers(); - invoker = MethodHandles.dropArguments(invokers0.uninitializedCallSite(), - 0, targetType.parameterList()); - assert(invoker.type().equals(targetType)); - uninitializedCallSite = invoker; - return invoker; - } - invoker = THROW_UCS; - if (invoker == null) { - try { - THROW_UCS = invoker = IMPL_LOOKUP - .findStatic(CallSite.class, "uninitializedCallSite", - MethodType.methodType(Empty.class)); - } catch (ReflectiveOperationException ex) { - throw newInternalError(ex); - } - } - invoker = MethodHandles.explicitCastArguments(invoker, MethodType.methodType(targetType.returnType())); - invoker = invoker.dropArguments(targetType, 0, targetType.parameterCount()); - assert(invoker.type().equals(targetType)); - uninitializedCallSite = invoker; - return invoker; + if (argType == Object.class) return Object[].class; + return Array.newInstance(argType, 0).getClass(); } public String toString() { @@ -457,4 +409,16 @@ } } + private static class Lazy { + private static final MethodHandle MH_asSpreader; + + static { + try { + MH_asSpreader = IMPL_LOOKUP.findVirtual(MethodHandle.class, "asSpreader", + MethodType.methodType(MethodHandle.class, Class.class, int.class)); + } catch (ReflectiveOperationException ex) { + throw newInternalError(ex); + } + } + } } diff -r d69abed3a07d -r 2d57604f9299 jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java Wed Sep 10 19:19:47 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java Wed Sep 10 19:19:48 2014 +0400 @@ -1151,7 +1151,7 @@ if (LambdaForm.signatureReturn(sig) == V_TYPE) srcType = srcType.changeReturnType(void.class); MethodTypeForm typeForm = srcType.form(); - typeForm.namedFunctionInvoker = DirectMethodHandle.make(m); + typeForm.setCachedMethodHandle(MethodTypeForm.MH_NF_INV, DirectMethodHandle.make(m)); } } } @@ -1249,15 +1249,16 @@ MethodType.methodType(Object.class, MethodHandle.class, Object[].class); private static MethodHandle computeInvoker(MethodTypeForm typeForm) { - MethodHandle mh = typeForm.namedFunctionInvoker; + typeForm = typeForm.basicType().form(); // normalize to basic type + MethodHandle mh = typeForm.cachedMethodHandle(MethodTypeForm.MH_NF_INV); if (mh != null) return mh; MemberName invoker = InvokerBytecodeGenerator.generateNamedFunctionInvoker(typeForm); // this could take a while mh = DirectMethodHandle.make(invoker); - MethodHandle mh2 = typeForm.namedFunctionInvoker; + MethodHandle mh2 = typeForm.cachedMethodHandle(MethodTypeForm.MH_NF_INV); if (mh2 != null) return mh2; // benign race if (!mh.type().equals(INVOKER_METHOD_TYPE)) throw newInternalError(mh.debugString()); - return typeForm.namedFunctionInvoker = mh; + return typeForm.setCachedMethodHandle(MethodTypeForm.MH_NF_INV, mh); } @Hidden diff -r d69abed3a07d -r 2d57604f9299 jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java Wed Sep 10 19:19:47 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java Wed Sep 10 19:19:48 2014 +0400 @@ -624,15 +624,8 @@ * @see MethodHandles#spreadInvoker */ public Object invokeWithArguments(Object... arguments) throws Throwable { - int argc = arguments == null ? 0 : arguments.length; - @SuppressWarnings("LocalVariableHidesMemberVariable") - MethodType type = type(); - if (type.parameterCount() != argc || isVarargsCollector()) { - // simulate invoke - return asType(MethodType.genericMethodType(argc)).invokeWithArguments(arguments); - } - MethodHandle invoker = type.invokers().varargsInvoker(); - return invoker.invokeExact(this, arguments); + MethodType invocationType = MethodType.genericMethodType(arguments == null ? 0 : arguments.length); + return invocationType.invokers().spreadInvoker(0).invokeExact(asType(invocationType), arguments); } /** diff -r d69abed3a07d -r 2d57604f9299 jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Wed Sep 10 19:19:47 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Wed Sep 10 19:19:48 2014 +0400 @@ -1882,6 +1882,7 @@ MethodHandle spreadInvoker(MethodType type, int leadingArgCount) { if (leadingArgCount < 0 || leadingArgCount > type.parameterCount()) throw newIllegalArgumentException("bad argument count", leadingArgCount); + type = type.asSpreaderType(Object[].class, type.parameterCount() - leadingArgCount); return type.invokers().spreadInvoker(leadingArgCount); } @@ -1961,7 +1962,7 @@ */ static public MethodHandle invoker(MethodType type) { - return type.invokers().generalInvoker(); + return type.invokers().genericInvoker(); } static /*non-public*/ diff -r d69abed3a07d -r 2d57604f9299 jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java Wed Sep 10 19:19:47 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java Wed Sep 10 19:19:48 2014 +0400 @@ -467,6 +467,38 @@ return dropParameterTypes(start, end).insertParameterTypes(start, ptypesToInsert); } + /** Replace the last arrayLength parameter types with the component type of arrayType. + * @param arrayType any array type + * @param arrayLength the number of parameter types to change + * @return the resulting type + */ + /*non-public*/ MethodType asSpreaderType(Class arrayType, int arrayLength) { + assert(parameterCount() >= arrayLength); + int spreadPos = ptypes.length - arrayLength; + if (arrayLength == 0) return this; // nothing to change + if (arrayType == Object[].class) { + if (isGeneric()) return this; // nothing to change + if (spreadPos == 0) { + // no leading arguments to preserve; go generic + MethodType res = genericMethodType(arrayLength); + if (rtype != Object.class) { + res = res.changeReturnType(rtype); + } + return res; + } + } + Class elemType = arrayType.getComponentType(); + assert(elemType != null); + for (int i = spreadPos; i < ptypes.length; i++) { + if (ptypes[i] != elemType) { + Class[] fixedPtypes = ptypes.clone(); + Arrays.fill(fixedPtypes, i, ptypes.length, elemType); + return methodType(rtype, fixedPtypes); + } + } + return this; // arguments check out; no change + } + /** * Finds or creates a method type with some parameter types omitted. * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}. @@ -574,6 +606,10 @@ return genericMethodType(parameterCount()); } + /*non-public*/ boolean isGeneric() { + return this == erase() && !hasPrimitives(); + } + /** * Converts all primitive types to their corresponding wrapper types. * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}. diff -r d69abed3a07d -r 2d57604f9299 jdk/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java Wed Sep 10 19:19:47 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java Wed Sep 10 19:19:48 2014 +0400 @@ -51,7 +51,13 @@ final MethodType basicType; // the canonical erasure, with primitives simplified // Cached adapter information: - @Stable MethodHandle namedFunctionInvoker; // cached helper for LF.NamedFunction + @Stable final MethodHandle[] methodHandles; + // Indexes into methodHandles: + static final int + MH_BASIC_INV = 0, // cached instance of MH.invokeBasic + MH_NF_INV = 1, // cached helper for LF.NamedFunction + MH_UNINIT_CS = 2, // uninitialized call site + MH_LIMIT = 3; // Cached lambda form information, for basic types only: final @Stable LambdaForm[] lambdaForms; @@ -98,6 +104,18 @@ return true; } + public MethodHandle cachedMethodHandle(int which) { + assert(assertIsBasicType()); + return methodHandles[which]; + } + + synchronized public MethodHandle setCachedMethodHandle(int which, MethodHandle mh) { + // Simulate a CAS, to avoid racy duplication of results. + MethodHandle prev = methodHandles[which]; + if (prev != null) return prev; + return methodHandles[which] = mh; + } + public LambdaForm cachedLambdaForm(int which) { assert(assertIsBasicType()); return lambdaForms[which]; @@ -169,6 +187,7 @@ this.argCounts = that.argCounts; this.argToSlotTable = that.argToSlotTable; this.slotToArgTable = that.slotToArgTable; + this.methodHandles = null; this.lambdaForms = null; return; } @@ -214,6 +233,7 @@ // Initialize caches, but only for basic types assert(basicType == erasedType); this.lambdaForms = new LambdaForm[LF_LIMIT]; + this.methodHandles = new MethodHandle[MH_LIMIT]; } private static long pack(int a, int b, int c, int d) {