8264288: Performance issue with MethodHandle.asCollector
Reviewed-by: jrose, vlivanov
This commit is contained in:
parent
920189918e
commit
b7baca7f32
src/java.base/share/classes/java/lang/invoke
InvokerBytecodeGenerator.javaLambdaForm.javaLambdaFormEditor.javaMethodHandle.javaMethodHandleImpl.javaMethodHandles.javaMethodTypeForm.java
test
jdk/java/lang/invoke
micro/org/openjdk/bench/java/lang/invoke
@ -869,13 +869,6 @@ class InvokerBytecodeGenerator {
|
||||
onStack = emitLoop(i);
|
||||
i += 2; // jump to the end of the LOOP idiom
|
||||
continue;
|
||||
case NEW_ARRAY:
|
||||
Class<?> rtype = name.function.methodType().returnType();
|
||||
if (isStaticallyNameable(rtype)) {
|
||||
emitNewArray(name);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case ARRAY_LOAD:
|
||||
emitArrayLoad(name);
|
||||
continue;
|
||||
@ -1112,43 +1105,6 @@ class InvokerBytecodeGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
void emitNewArray(Name name) throws InternalError {
|
||||
Class<?> rtype = name.function.methodType().returnType();
|
||||
if (name.arguments.length == 0) {
|
||||
// The array will be a constant.
|
||||
Object emptyArray;
|
||||
try {
|
||||
emptyArray = name.function.resolvedHandle().invoke();
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
assert(java.lang.reflect.Array.getLength(emptyArray) == 0);
|
||||
assert(emptyArray.getClass() == rtype); // exact typing
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, className, classData(emptyArray), "Ljava/lang/Object;");
|
||||
emitReferenceCast(rtype, emptyArray);
|
||||
return;
|
||||
}
|
||||
Class<?> arrayElementType = rtype.getComponentType();
|
||||
assert(arrayElementType != null);
|
||||
emitIconstInsn(name.arguments.length);
|
||||
int xas = Opcodes.AASTORE;
|
||||
if (!arrayElementType.isPrimitive()) {
|
||||
mv.visitTypeInsn(Opcodes.ANEWARRAY, getInternalName(arrayElementType));
|
||||
} else {
|
||||
byte tc = arrayTypeCode(Wrapper.forPrimitiveType(arrayElementType));
|
||||
xas = arrayInsnOpcode(tc, xas);
|
||||
mv.visitIntInsn(Opcodes.NEWARRAY, tc);
|
||||
}
|
||||
// store arguments
|
||||
for (int i = 0; i < name.arguments.length; i++) {
|
||||
mv.visitInsn(Opcodes.DUP);
|
||||
emitIconstInsn(i);
|
||||
emitPushArgument(name, i);
|
||||
mv.visitInsn(xas);
|
||||
}
|
||||
// the array is left on the stack
|
||||
assertStaticType(rtype, name);
|
||||
}
|
||||
int refKindOpcode(byte refKind) {
|
||||
switch (refKind) {
|
||||
case REF_invokeVirtual: return Opcodes.INVOKEVIRTUAL;
|
||||
|
@ -315,6 +315,7 @@ class LambdaForm {
|
||||
PUT_DOUBLE_VOLATILE("putDoubleVolatile"),
|
||||
TRY_FINALLY("tryFinally"),
|
||||
COLLECT("collect"),
|
||||
COLLECTOR("collector"),
|
||||
CONVERT("convert"),
|
||||
SPREAD("spread"),
|
||||
LOOP("loop"),
|
||||
|
@ -648,57 +648,6 @@ class LambdaFormEditor {
|
||||
return putInCache(key, form);
|
||||
}
|
||||
|
||||
LambdaForm collectArgumentArrayForm(int pos, MethodHandle arrayCollector) {
|
||||
MethodType collectorType = arrayCollector.type();
|
||||
int collectorArity = collectorType.parameterCount();
|
||||
assert(arrayCollector.intrinsicName() == Intrinsic.NEW_ARRAY);
|
||||
Class<?> arrayType = collectorType.returnType();
|
||||
Class<?> elementType = arrayType.getComponentType();
|
||||
BasicType argType = basicType(elementType);
|
||||
int argTypeKey = argType.ordinal();
|
||||
if (argType.basicTypeClass() != elementType) {
|
||||
// return null if it requires more metadata (like String[].class)
|
||||
if (!elementType.isPrimitive())
|
||||
return null;
|
||||
argTypeKey = TYPE_LIMIT + Wrapper.forPrimitiveType(elementType).ordinal();
|
||||
}
|
||||
assert(collectorType.parameterList().equals(Collections.nCopies(collectorArity, elementType)));
|
||||
byte kind = COLLECT_ARGS_TO_ARRAY;
|
||||
TransformKey key = TransformKey.of(kind, pos, collectorArity, argTypeKey);
|
||||
LambdaForm form = getInCache(key);
|
||||
if (form != null) {
|
||||
assert(form.arity == lambdaForm.arity - 1 + collectorArity);
|
||||
return form;
|
||||
}
|
||||
LambdaFormBuffer buf = buffer();
|
||||
buf.startEdit();
|
||||
|
||||
assert(pos + 1 <= lambdaForm.arity);
|
||||
assert(pos > 0); // cannot filter the MH arg itself
|
||||
|
||||
Name[] newParams = new Name[collectorArity];
|
||||
for (int i = 0; i < collectorArity; i++) {
|
||||
newParams[i] = new Name(pos + i, argType);
|
||||
}
|
||||
Name callCombiner = new Name(new NamedFunction(arrayCollector, Intrinsic.NEW_ARRAY),
|
||||
(Object[]) /*...*/ newParams);
|
||||
|
||||
// insert the new expression
|
||||
int exprPos = lambdaForm.arity();
|
||||
buf.insertExpression(exprPos, callCombiner);
|
||||
|
||||
// insert new arguments
|
||||
int argPos = pos + 1; // skip result parameter
|
||||
for (Name newParam : newParams) {
|
||||
buf.insertParameter(argPos++, newParam);
|
||||
}
|
||||
assert(buf.lastIndexOf(callCombiner) == exprPos+newParams.length);
|
||||
buf.replaceParameterByCopy(pos, exprPos+newParams.length);
|
||||
|
||||
form = buf.endEdit();
|
||||
return putInCache(key, form);
|
||||
}
|
||||
|
||||
LambdaForm filterArgumentForm(int pos, BasicType newType) {
|
||||
TransformKey key = TransformKey.of(FILTER_ARG, pos, newType.ordinal());
|
||||
LambdaForm form = getInCache(key);
|
||||
|
@ -1244,13 +1244,9 @@ assertEquals("[123]", (String) longsToString.invokeExact((long)123));
|
||||
asCollectorChecks(arrayType, collectArgPos, arrayLength);
|
||||
BoundMethodHandle mh = rebind();
|
||||
MethodType resultType = type().asCollectorType(arrayType, collectArgPos, arrayLength);
|
||||
MethodHandle newArray = MethodHandleImpl.varargsArray(arrayType, arrayLength);
|
||||
LambdaForm lform = mh.editor().collectArgumentArrayForm(1 + collectArgPos, newArray);
|
||||
if (lform != null) {
|
||||
return mh.copyWith(resultType, lform);
|
||||
}
|
||||
lform = mh.editor().collectArgumentsForm(1 + collectArgPos, newArray.type().basicType());
|
||||
return mh.copyWithExtendL(resultType, lform, newArray);
|
||||
MethodHandle collector = MethodHandleImpl.varargsArray(arrayType, arrayLength);
|
||||
LambdaForm lform = mh.editor().collectArgumentsForm(1 + collectArgPos, collector.type().basicType());
|
||||
return mh.copyWithExtendL(resultType, lform, collector);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1227,7 +1227,6 @@ abstract class MethodHandleImpl {
|
||||
GUARD_WITH_CATCH,
|
||||
TRY_FINALLY,
|
||||
LOOP,
|
||||
NEW_ARRAY,
|
||||
ARRAY_LOAD,
|
||||
ARRAY_STORE,
|
||||
ARRAY_LENGTH,
|
||||
@ -1292,120 +1291,8 @@ abstract class MethodHandleImpl {
|
||||
return new IntrinsicMethodHandle(SimpleMethodHandle.make(type, form), intrinsicName);
|
||||
}
|
||||
|
||||
/// Collection of multiple arguments.
|
||||
|
||||
private static MethodHandle findCollector(String name, int nargs, Class<?> rtype, Class<?>... ptypes) {
|
||||
MethodType type = MethodType.genericMethodType(nargs)
|
||||
.changeReturnType(rtype)
|
||||
.insertParameterTypes(0, ptypes);
|
||||
try {
|
||||
return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, name, type);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Object[] NO_ARGS_ARRAY = {};
|
||||
private static Object[] makeArray(Object... args) { return args; }
|
||||
private static Object[] array() { return NO_ARGS_ARRAY; }
|
||||
private static Object[] array(Object a0)
|
||||
{ return makeArray(a0); }
|
||||
private static Object[] array(Object a0, Object a1)
|
||||
{ return makeArray(a0, a1); }
|
||||
private static Object[] array(Object a0, Object a1, Object a2)
|
||||
{ return makeArray(a0, a1, a2); }
|
||||
private static Object[] array(Object a0, Object a1, Object a2, Object a3)
|
||||
{ return makeArray(a0, a1, a2, a3); }
|
||||
private static Object[] array(Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4)
|
||||
{ return makeArray(a0, a1, a2, a3, a4); }
|
||||
private static Object[] array(Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4, Object a5)
|
||||
{ return makeArray(a0, a1, a2, a3, a4, a5); }
|
||||
private static Object[] array(Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4, Object a5, Object a6)
|
||||
{ return makeArray(a0, a1, a2, a3, a4, a5, a6); }
|
||||
private static Object[] array(Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4, Object a5, Object a6, Object a7)
|
||||
{ return makeArray(a0, a1, a2, a3, a4, a5, a6, a7); }
|
||||
private static Object[] array(Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4, Object a5, Object a6, Object a7,
|
||||
Object a8)
|
||||
{ return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8); }
|
||||
private static Object[] array(Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4, Object a5, Object a6, Object a7,
|
||||
Object a8, Object a9)
|
||||
{ return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
|
||||
|
||||
private static final int ARRAYS_COUNT = 11;
|
||||
private static final @Stable MethodHandle[] ARRAYS = new MethodHandle[MAX_ARITY + 1];
|
||||
|
||||
// filling versions of the above:
|
||||
// using Integer len instead of int len and no varargs to avoid bootstrapping problems
|
||||
private static Object[] fillNewArray(Integer len, Object[] /*not ...*/ args) {
|
||||
Object[] a = new Object[len];
|
||||
fillWithArguments(a, 0, args);
|
||||
return a;
|
||||
}
|
||||
private static Object[] fillNewTypedArray(Object[] example, Integer len, Object[] /*not ...*/ args) {
|
||||
Object[] a = Arrays.copyOf(example, len);
|
||||
assert(a.getClass() != Object[].class);
|
||||
fillWithArguments(a, 0, args);
|
||||
return a;
|
||||
}
|
||||
private static void fillWithArguments(Object[] a, int pos, Object... args) {
|
||||
System.arraycopy(args, 0, a, pos, args.length);
|
||||
}
|
||||
// using Integer pos instead of int pos to avoid bootstrapping problems
|
||||
private static Object[] fillArray(Integer pos, Object[] a, Object a0)
|
||||
{ fillWithArguments(a, pos, a0); return a; }
|
||||
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1)
|
||||
{ fillWithArguments(a, pos, a0, a1); return a; }
|
||||
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2)
|
||||
{ fillWithArguments(a, pos, a0, a1, a2); return a; }
|
||||
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3)
|
||||
{ fillWithArguments(a, pos, a0, a1, a2, a3); return a; }
|
||||
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4)
|
||||
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4); return a; }
|
||||
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4, Object a5)
|
||||
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5); return a; }
|
||||
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4, Object a5, Object a6)
|
||||
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6); return a; }
|
||||
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4, Object a5, Object a6, Object a7)
|
||||
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7); return a; }
|
||||
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4, Object a5, Object a6, Object a7,
|
||||
Object a8)
|
||||
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8); return a; }
|
||||
private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3,
|
||||
Object a4, Object a5, Object a6, Object a7,
|
||||
Object a8, Object a9)
|
||||
{ fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); return a; }
|
||||
|
||||
private static final int FILL_ARRAYS_COUNT = 11; // current number of fillArray methods
|
||||
private static final @Stable MethodHandle[] FILL_ARRAYS = new MethodHandle[FILL_ARRAYS_COUNT];
|
||||
|
||||
private static MethodHandle getFillArray(int count) {
|
||||
assert (count > 0 && count < FILL_ARRAYS_COUNT);
|
||||
MethodHandle mh = FILL_ARRAYS[count];
|
||||
if (mh != null) {
|
||||
return mh;
|
||||
}
|
||||
mh = findCollector("fillArray", count, Object[].class, Integer.class, Object[].class);
|
||||
FILL_ARRAYS[count] = mh;
|
||||
return mh;
|
||||
}
|
||||
|
||||
private static Object copyAsPrimitiveArray(Wrapper w, Object... boxes) {
|
||||
Object a = w.makeArray(boxes.length);
|
||||
w.copyArrayUnboxing(boxes, 0, a, 0, boxes.length);
|
||||
return a;
|
||||
}
|
||||
|
||||
/** Return a method handle that takes the indicated number of Object
|
||||
* arguments and returns an Object array of them, as if for varargs.
|
||||
*/
|
||||
@ -1414,97 +1301,11 @@ abstract class MethodHandleImpl {
|
||||
if (mh != null) {
|
||||
return mh;
|
||||
}
|
||||
if (nargs < ARRAYS_COUNT) {
|
||||
mh = findCollector("array", nargs, Object[].class);
|
||||
} else {
|
||||
mh = buildVarargsArray(getConstantHandle(MH_fillNewArray),
|
||||
getConstantHandle(MH_arrayIdentity), nargs);
|
||||
}
|
||||
mh = makeCollector(Object[].class, nargs);
|
||||
assert(assertCorrectArity(mh, nargs));
|
||||
mh = makeIntrinsic(mh, Intrinsic.NEW_ARRAY);
|
||||
return ARRAYS[nargs] = mh;
|
||||
}
|
||||
|
||||
private static boolean assertCorrectArity(MethodHandle mh, int arity) {
|
||||
assert(mh.type().parameterCount() == arity) : "arity != "+arity+": "+mh;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Array identity function (used as getConstantHandle(MH_arrayIdentity)).
|
||||
static <T> T[] identity(T[] x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
private static MethodHandle buildVarargsArray(MethodHandle newArray, MethodHandle finisher, int nargs) {
|
||||
// Build up the result mh as a sequence of fills like this:
|
||||
// finisher(fill(fill(newArrayWA(23,x1..x10),10,x11..x20),20,x21..x23))
|
||||
// The various fill(_,10*I,___*[J]) are reusable.
|
||||
int leftLen = Math.min(nargs, LEFT_ARGS); // absorb some arguments immediately
|
||||
int rightLen = nargs - leftLen;
|
||||
MethodHandle leftCollector = newArray.bindTo(nargs);
|
||||
leftCollector = leftCollector.asCollector(Object[].class, leftLen);
|
||||
MethodHandle mh = finisher;
|
||||
if (rightLen > 0) {
|
||||
MethodHandle rightFiller = fillToRight(LEFT_ARGS + rightLen);
|
||||
if (mh.equals(getConstantHandle(MH_arrayIdentity)))
|
||||
mh = rightFiller;
|
||||
else
|
||||
mh = MethodHandles.collectArguments(mh, 0, rightFiller);
|
||||
}
|
||||
if (mh.equals(getConstantHandle(MH_arrayIdentity)))
|
||||
mh = leftCollector;
|
||||
else
|
||||
mh = MethodHandles.collectArguments(mh, 0, leftCollector);
|
||||
return mh;
|
||||
}
|
||||
|
||||
private static final int LEFT_ARGS = FILL_ARRAYS_COUNT - 1;
|
||||
private static final @Stable MethodHandle[] FILL_ARRAY_TO_RIGHT = new MethodHandle[MAX_ARITY + 1];
|
||||
/** fill_array_to_right(N).invoke(a, argL..arg[N-1])
|
||||
* fills a[L]..a[N-1] with corresponding arguments,
|
||||
* and then returns a. The value L is a global constant (LEFT_ARGS).
|
||||
*/
|
||||
private static MethodHandle fillToRight(int nargs) {
|
||||
MethodHandle filler = FILL_ARRAY_TO_RIGHT[nargs];
|
||||
if (filler != null) return filler;
|
||||
filler = buildFiller(nargs);
|
||||
assert(assertCorrectArity(filler, nargs - LEFT_ARGS + 1));
|
||||
return FILL_ARRAY_TO_RIGHT[nargs] = filler;
|
||||
}
|
||||
private static MethodHandle buildFiller(int nargs) {
|
||||
if (nargs <= LEFT_ARGS)
|
||||
return getConstantHandle(MH_arrayIdentity); // no args to fill; return the array unchanged
|
||||
// we need room for both mh and a in mh.invoke(a, arg*[nargs])
|
||||
final int CHUNK = LEFT_ARGS;
|
||||
int rightLen = nargs % CHUNK;
|
||||
int midLen = nargs - rightLen;
|
||||
if (rightLen == 0) {
|
||||
midLen = nargs - (rightLen = CHUNK);
|
||||
if (FILL_ARRAY_TO_RIGHT[midLen] == null) {
|
||||
// build some precursors from left to right
|
||||
for (int j = LEFT_ARGS % CHUNK; j < midLen; j += CHUNK)
|
||||
if (j > LEFT_ARGS) fillToRight(j);
|
||||
}
|
||||
}
|
||||
if (midLen < LEFT_ARGS) rightLen = nargs - (midLen = LEFT_ARGS);
|
||||
assert(rightLen > 0);
|
||||
MethodHandle midFill = fillToRight(midLen); // recursive fill
|
||||
MethodHandle rightFill = getFillArray(rightLen).bindTo(midLen); // [midLen..nargs-1]
|
||||
assert(midFill.type().parameterCount() == 1 + midLen - LEFT_ARGS);
|
||||
assert(rightFill.type().parameterCount() == 1 + rightLen);
|
||||
|
||||
// Combine the two fills:
|
||||
// right(mid(a, x10..x19), x20..x23)
|
||||
// The final product will look like this:
|
||||
// right(mid(newArrayLeft(24, x0..x9), x10..x19), x20..x23)
|
||||
if (midLen == LEFT_ARGS)
|
||||
return rightFill;
|
||||
else
|
||||
return MethodHandles.collectArguments(rightFill, 0, midFill);
|
||||
}
|
||||
|
||||
static final int MAX_JVM_ARITY = 255; // limit imposed by the JVM
|
||||
|
||||
/** Return a method handle that takes the indicated number of
|
||||
* typed arguments and returns an array of them.
|
||||
* The type argument is the array type.
|
||||
@ -1512,7 +1313,6 @@ abstract class MethodHandleImpl {
|
||||
static MethodHandle varargsArray(Class<?> arrayType, int nargs) {
|
||||
Class<?> elemType = arrayType.getComponentType();
|
||||
if (elemType == null) throw new IllegalArgumentException("not an array: "+arrayType);
|
||||
// FIXME: Need more special casing and caching here.
|
||||
if (nargs >= MAX_JVM_ARITY/2 - 1) {
|
||||
int slots = nargs;
|
||||
final int MAX_ARRAY_SLOTS = MAX_JVM_ARITY - 1; // 1 for receiver MH
|
||||
@ -1527,34 +1327,20 @@ abstract class MethodHandleImpl {
|
||||
MethodHandle cache[] = Makers.TYPED_COLLECTORS.get(elemType);
|
||||
MethodHandle mh = nargs < cache.length ? cache[nargs] : null;
|
||||
if (mh != null) return mh;
|
||||
if (nargs == 0) {
|
||||
Object example = java.lang.reflect.Array.newInstance(arrayType.getComponentType(), 0);
|
||||
mh = MethodHandles.constant(arrayType, example);
|
||||
} else if (elemType.isPrimitive()) {
|
||||
MethodHandle builder = getConstantHandle(MH_fillNewArray);
|
||||
MethodHandle producer = buildArrayProducer(arrayType);
|
||||
mh = buildVarargsArray(builder, producer, nargs);
|
||||
} else {
|
||||
Class<? extends Object[]> objArrayType = arrayType.asSubclass(Object[].class);
|
||||
Object[] example = Arrays.copyOf(NO_ARGS_ARRAY, 0, objArrayType);
|
||||
MethodHandle builder = getConstantHandle(MH_fillNewTypedArray).bindTo(example);
|
||||
MethodHandle producer = getConstantHandle(MH_arrayIdentity); // must be weakly typed
|
||||
mh = buildVarargsArray(builder, producer, nargs);
|
||||
}
|
||||
mh = mh.asType(MethodType.methodType(arrayType, Collections.<Class<?>>nCopies(nargs, elemType)));
|
||||
mh = makeIntrinsic(mh, Intrinsic.NEW_ARRAY);
|
||||
mh = makeCollector(arrayType, nargs);
|
||||
assert(assertCorrectArity(mh, nargs));
|
||||
if (nargs < cache.length)
|
||||
cache[nargs] = mh;
|
||||
return mh;
|
||||
}
|
||||
|
||||
private static MethodHandle buildArrayProducer(Class<?> arrayType) {
|
||||
Class<?> elemType = arrayType.getComponentType();
|
||||
assert(elemType.isPrimitive());
|
||||
return getConstantHandle(MH_copyAsPrimitiveArray).bindTo(Wrapper.forPrimitiveType(elemType));
|
||||
private static boolean assertCorrectArity(MethodHandle mh, int arity) {
|
||||
assert(mh.type().parameterCount() == arity) : "arity != "+arity+": "+mh;
|
||||
return true;
|
||||
}
|
||||
|
||||
static final int MAX_JVM_ARITY = 255; // limit imposed by the JVM
|
||||
|
||||
/*non-public*/
|
||||
static void assertSame(Object mh1, Object mh2) {
|
||||
if (mh1 != mh2) {
|
||||
@ -2089,21 +1875,94 @@ abstract class MethodHandleImpl {
|
||||
return r;
|
||||
}
|
||||
|
||||
// see varargsArray method for chaching/package-private version of this
|
||||
private static MethodHandle makeCollector(Class<?> arrayType, int parameterCount) {
|
||||
MethodType type = MethodType.methodType(arrayType, Collections.nCopies(parameterCount, arrayType.componentType()));
|
||||
MethodHandle newArray = MethodHandles.arrayConstructor(arrayType);
|
||||
|
||||
LambdaForm form = makeCollectorForm(type.basicType(), arrayType);
|
||||
|
||||
BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_L();
|
||||
BoundMethodHandle mh;
|
||||
try {
|
||||
mh = (BoundMethodHandle) data.factory().invokeBasic(type, form, (Object) newArray);
|
||||
} catch (Throwable ex) {
|
||||
throw uncaughtException(ex);
|
||||
}
|
||||
assert(mh.type() == type);
|
||||
return mh;
|
||||
}
|
||||
|
||||
private static LambdaForm makeCollectorForm(MethodType basicType, Class<?> arrayType) {
|
||||
MethodType lambdaType = basicType.invokerType();
|
||||
int parameterCount = basicType.parameterCount();
|
||||
|
||||
// Only share the lambda form for empty arrays and reference types.
|
||||
// Sharing based on the basic type alone doesn't work because
|
||||
// we need a separate lambda form for byte/short/char/int which
|
||||
// are all erased to int otherwise.
|
||||
// Other caching for primitive types happens at the MethodHandle level (see varargsArray).
|
||||
boolean isReferenceType = !arrayType.componentType().isPrimitive();
|
||||
boolean isSharedLambdaForm = parameterCount == 0 || isReferenceType;
|
||||
if (isSharedLambdaForm) {
|
||||
LambdaForm lform = basicType.form().cachedLambdaForm(MethodTypeForm.LF_COLLECTOR);
|
||||
if (lform != null) {
|
||||
return lform;
|
||||
}
|
||||
}
|
||||
|
||||
// use erased accessor for reference types
|
||||
MethodHandle storeFunc = isReferenceType
|
||||
? ArrayAccessor.OBJECT_ARRAY_SETTER
|
||||
: makeArrayElementAccessor(arrayType, ArrayAccess.SET);
|
||||
|
||||
final int THIS_MH = 0; // the BMH_L
|
||||
final int ARG_BASE = 1; // start of incoming arguments
|
||||
final int ARG_LIMIT = ARG_BASE + parameterCount;
|
||||
|
||||
int nameCursor = ARG_LIMIT;
|
||||
final int GET_NEW_ARRAY = nameCursor++;
|
||||
final int CALL_NEW_ARRAY = nameCursor++;
|
||||
final int STORE_ELEMENT_BASE = nameCursor;
|
||||
final int STORE_ELEMENT_LIMIT = STORE_ELEMENT_BASE + parameterCount;
|
||||
nameCursor = STORE_ELEMENT_LIMIT;
|
||||
|
||||
Name[] names = arguments(nameCursor - ARG_LIMIT, lambdaType);
|
||||
|
||||
BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_L();
|
||||
names[THIS_MH] = names[THIS_MH].withConstraint(data);
|
||||
names[GET_NEW_ARRAY] = new Name(data.getterFunction(0), names[THIS_MH]);
|
||||
|
||||
MethodHandle invokeBasic = MethodHandles.basicInvoker(MethodType.methodType(Object.class, int.class));
|
||||
names[CALL_NEW_ARRAY] = new Name(new NamedFunction(invokeBasic), names[GET_NEW_ARRAY], parameterCount);
|
||||
for (int storeIndex = 0,
|
||||
storeNameCursor = STORE_ELEMENT_BASE,
|
||||
argCursor = ARG_BASE;
|
||||
storeNameCursor < STORE_ELEMENT_LIMIT;
|
||||
storeIndex++, storeNameCursor++, argCursor++){
|
||||
|
||||
names[storeNameCursor] = new Name(new NamedFunction(storeFunc, Intrinsic.ARRAY_STORE),
|
||||
names[CALL_NEW_ARRAY], storeIndex, names[argCursor]);
|
||||
}
|
||||
|
||||
LambdaForm lform = new LambdaForm(lambdaType.parameterCount(), names, CALL_NEW_ARRAY, Kind.COLLECTOR);
|
||||
if (isSharedLambdaForm) {
|
||||
lform = basicType.form().setCachedLambdaForm(MethodTypeForm.LF_COLLECTOR, lform);
|
||||
}
|
||||
return lform;
|
||||
}
|
||||
|
||||
// Indexes into constant method handles:
|
||||
static final int
|
||||
MH_cast = 0,
|
||||
MH_selectAlternative = 1,
|
||||
MH_copyAsPrimitiveArray = 2,
|
||||
MH_fillNewTypedArray = 3,
|
||||
MH_fillNewArray = 4,
|
||||
MH_arrayIdentity = 5,
|
||||
MH_countedLoopPred = 6,
|
||||
MH_countedLoopStep = 7,
|
||||
MH_initIterator = 8,
|
||||
MH_iteratePred = 9,
|
||||
MH_iterateNext = 10,
|
||||
MH_Array_newInstance = 11,
|
||||
MH_LIMIT = 12;
|
||||
MH_cast = 0,
|
||||
MH_selectAlternative = 1,
|
||||
MH_countedLoopPred = 2,
|
||||
MH_countedLoopStep = 3,
|
||||
MH_initIterator = 4,
|
||||
MH_iteratePred = 5,
|
||||
MH_iterateNext = 6,
|
||||
MH_Array_newInstance = 7,
|
||||
MH_LIMIT = 8;
|
||||
|
||||
static MethodHandle getConstantHandle(int idx) {
|
||||
MethodHandle handle = HANDLES[idx];
|
||||
@ -2132,18 +1991,6 @@ abstract class MethodHandleImpl {
|
||||
case MH_cast:
|
||||
return IMPL_LOOKUP.findVirtual(Class.class, "cast",
|
||||
MethodType.methodType(Object.class, Object.class));
|
||||
case MH_copyAsPrimitiveArray:
|
||||
return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "copyAsPrimitiveArray",
|
||||
MethodType.methodType(Object.class, Wrapper.class, Object[].class));
|
||||
case MH_arrayIdentity:
|
||||
return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "identity",
|
||||
MethodType.methodType(Object[].class, Object[].class));
|
||||
case MH_fillNewArray:
|
||||
return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "fillNewArray",
|
||||
MethodType.methodType(Object[].class, Integer.class, Object[].class));
|
||||
case MH_fillNewTypedArray:
|
||||
return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "fillNewTypedArray",
|
||||
MethodType.methodType(Object[].class, Object[].class, Integer.class, Object[].class));
|
||||
case MH_selectAlternative:
|
||||
return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "selectAlternative",
|
||||
MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class));
|
||||
|
@ -5731,14 +5731,7 @@ assertEquals("[top, [[up, down, strange], charm], bottom]",
|
||||
MethodType newType = collectArgumentsChecks(target, pos, filter);
|
||||
MethodType collectorType = filter.type();
|
||||
BoundMethodHandle result = target.rebind();
|
||||
LambdaForm lform;
|
||||
if (collectorType.returnType().isArray() && filter.intrinsicName() == Intrinsic.NEW_ARRAY) {
|
||||
lform = result.editor().collectArgumentArrayForm(1 + pos, filter);
|
||||
if (lform != null) {
|
||||
return result.copyWith(newType, lform);
|
||||
}
|
||||
}
|
||||
lform = result.editor().collectArgumentsForm(1 + pos, collectorType.basicType());
|
||||
LambdaForm lform = result.editor().collectArgumentsForm(1 + pos, collectorType.basicType());
|
||||
return result.copyWithExtendL(newType, lform, filter);
|
||||
}
|
||||
|
||||
|
@ -90,7 +90,8 @@ final class MethodTypeForm {
|
||||
LF_VH_EX_INVOKER = 22, // VarHandle exact invoker
|
||||
LF_VH_GEN_INVOKER = 23, // VarHandle generic invoker
|
||||
LF_VH_GEN_LINKER = 24, // VarHandle generic linker
|
||||
LF_LIMIT = 25;
|
||||
LF_COLLECTOR = 25, // collector handle
|
||||
LF_LIMIT = 26;
|
||||
|
||||
/** 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.
|
||||
|
@ -113,7 +113,7 @@ public class MethodHandlesArityLimitsTest {
|
||||
asList.asType(MethodType.genericMethodType(254));//does not throw IAE or WMTE
|
||||
}
|
||||
catch(WrongMethodTypeException wmte) {
|
||||
Assert.fail("Unexpected WrongMethodTypeException thrown");
|
||||
throw new AssertionError("Unexpected WrongMethodTypeException thrown", wmte);
|
||||
}
|
||||
asList.asType(MethodType.genericMethodType(255));//throws WMTE
|
||||
}
|
||||
|
@ -76,10 +76,13 @@ public class VarargsArrayTest {
|
||||
}
|
||||
}
|
||||
|
||||
private static class CustomClass {}
|
||||
|
||||
public static void testVarargsReferenceArray() throws Throwable {
|
||||
testTypedVarargsArray(Object[].class);
|
||||
testTypedVarargsArray(String[].class);
|
||||
testTypedVarargsArray(Number[].class);
|
||||
testTypedVarargsArray(CustomClass[].class);
|
||||
}
|
||||
|
||||
public static void testVarargsPrimitiveArray() throws Throwable {
|
||||
@ -171,6 +174,8 @@ public class VarargsArrayTest {
|
||||
if (elem == null) {
|
||||
if (elemType == String.class)
|
||||
arg = "#"+arg;
|
||||
if (elemType == CustomClass.class)
|
||||
arg = new CustomClass();
|
||||
arg = elemType.cast(arg); // just to make sure
|
||||
} else {
|
||||
switch (elem) {
|
||||
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package org.openjdk.bench.java.lang.invoke;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
|
||||
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
|
||||
@State(org.openjdk.jmh.annotations.Scope.Thread)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
@Fork(3)
|
||||
public class TypedAsCollector {
|
||||
|
||||
static final MethodHandle MH_COLLECT_OBJECT = MethodHandles.identity(Object[].class).asCollector(Object[].class, 3);
|
||||
static final MethodHandle MH_COLLECT_STRING = MethodHandles.identity(String[].class).asCollector(String[].class, 3);
|
||||
static final MethodHandle MH_COLLECT_INT = MethodHandles.identity(int[].class).asCollector(int[].class, 3);
|
||||
|
||||
// uses a different code path to construct the collector
|
||||
static final MethodHandle MH_COLLECT_OBJECT_HA = MethodHandles.identity(Object[].class).asCollector(Object[].class, 12);
|
||||
static final MethodHandle MH_COLLECT_STRING_HA = MethodHandles.identity(String[].class).asCollector(String[].class, 12);
|
||||
static final MethodHandle MH_COLLECT_INT_HA = MethodHandles.identity(int[].class).asCollector(int[].class, 12);
|
||||
|
||||
@Benchmark
|
||||
public Object[] testObjectCollect() throws Throwable {
|
||||
return (Object[]) MH_COLLECT_OBJECT.invokeExact((Object) "A", (Object) "B", (Object) "C");
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Object[] testStringCollect() throws Throwable {
|
||||
return (String[]) MH_COLLECT_STRING.invokeExact("A", "B", "C");
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int[] testIntCollect() throws Throwable {
|
||||
return (int[]) MH_COLLECT_INT.invokeExact(1, 2, 3);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Object[] testObjectCollectHighArity() throws Throwable {
|
||||
return (Object[]) MH_COLLECT_OBJECT_HA.invokeExact(
|
||||
(Object) "A", (Object) "B", (Object) "C", (Object) "D", (Object) "E", (Object) "F",
|
||||
(Object) "G", (Object) "H", (Object) "I", (Object) "J", (Object) "K", (Object) "L");
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Object[] testStringCollectHighArity() throws Throwable {
|
||||
return (String[]) MH_COLLECT_STRING_HA.invokeExact(
|
||||
"A", "B", "C", "D", "E", "F",
|
||||
"G", "H", "I", "J", "K", "L");
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int[] testIntCollectHighArity() throws Throwable {
|
||||
return (int[]) MH_COLLECT_INT_HA.invokeExact(
|
||||
1, 2, 3, 4, 5, 6,
|
||||
7, 8, 9, 10, 11, 12);
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user