8050053: Improve caching of different invokers
Reviewed-by: vlivanov, psandoz
Contributed-by: john.r.rose@oracle.com
--- 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 {
--- 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.<init>.
/*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);
+ }
+ }
+ }
}
--- 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
--- 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);
}
/**
--- 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*/
--- 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}.
--- 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) {