From b8ed9e1ef77812d5d628139023f603a4e54fcd08 Mon Sep 17 00:00:00 2001 From: vlivanov Date: Wed, 10 Sep 2014 18:34:42 +0400 Subject: 8057922: Improve LambdaForm sharing by using LambdaFormEditor more extensively Reviewed-by: vlivanov, psandoz Contributed-by: john.r.rose@oracle.com --- src/share/classes/java/lang/invoke/LambdaForm.java | 22 -- .../classes/java/lang/invoke/LambdaFormBuffer.java | 5 +- .../classes/java/lang/invoke/LambdaFormEditor.java | 379 ++++++++++++++++++++- .../classes/java/lang/invoke/MethodHandle.java | 39 ++- .../classes/java/lang/invoke/MethodHandleImpl.java | 63 +++- .../java/lang/invoke/MethodHandleStatics.java | 13 +- .../classes/java/lang/invoke/MethodHandles.java | 221 ++++++++++-- 7 files changed, 676 insertions(+), 66 deletions(-) (limited to 'src/share/classes/java/lang') diff --git a/src/share/classes/java/lang/invoke/LambdaForm.java b/src/share/classes/java/lang/invoke/LambdaForm.java index 3040b2e639..b83040647c 100644 --- a/src/share/classes/java/lang/invoke/LambdaForm.java +++ b/src/share/classes/java/lang/invoke/LambdaForm.java @@ -802,28 +802,6 @@ class LambdaForm { return rval; } - //** This transform is applied (statically) to every name.function. */ - /* - private static MethodHandle eraseSubwordTypes(MethodHandle mh) { - MethodType mt = mh.type(); - if (mt.hasPrimitives()) { - mt = mt.changeReturnType(eraseSubwordType(mt.returnType())); - for (int i = 0; i < mt.parameterCount(); i++) { - mt = mt.changeParameterType(i, eraseSubwordType(mt.parameterType(i))); - } - mh = MethodHandles.explicitCastArguments(mh, mt); - } - return mh; - } - private static Class eraseSubwordType(Class type) { - if (!type.isPrimitive()) return type; - if (type == int.class) return type; - Wrapper w = Wrapper.forPrimitiveType(type); - if (w.isSubwordOrInt()) return int.class; - return type; - } - */ - static void traceInterpreter(String event, Object obj, Object... args) { if (TRACE_INTERPRETER) { System.out.println("LFI: "+event+" "+(obj != null ? obj : "")+(args != null && args.length != 0 ? Arrays.asList(args) : "")); diff --git a/src/share/classes/java/lang/invoke/LambdaFormBuffer.java b/src/share/classes/java/lang/invoke/LambdaFormBuffer.java index efd6cc97e4..872a2a5992 100644 --- a/src/share/classes/java/lang/invoke/LambdaFormBuffer.java +++ b/src/share/classes/java/lang/invoke/LambdaFormBuffer.java @@ -59,7 +59,7 @@ final class LambdaFormBuffer { resultName = names[result]; } - LambdaForm lambdaForm() { + private LambdaForm lambdaForm() { assert(!inTrans()); // need endEdit call to tidy things up return new LambdaForm(debugName, arity, nameArray(), resultIndex()); } @@ -276,7 +276,7 @@ final class LambdaFormBuffer { } /** Finish a transaction. */ - void endEdit() { + LambdaForm endEdit() { assert(verifyFirstChange()); // Assuming names have been changed pairwise from originalNames[i] to names[i], // update arguments to ensure referential integrity. @@ -316,6 +316,7 @@ final class LambdaFormBuffer { arity -= exprp; } assert(verifyArity()); + return lambdaForm(); } private Name[] copyNamesInto(Name[] buffer) { diff --git a/src/share/classes/java/lang/invoke/LambdaFormEditor.java b/src/share/classes/java/lang/invoke/LambdaFormEditor.java index e12db5905f..1c29d73b4c 100644 --- a/src/share/classes/java/lang/invoke/LambdaFormEditor.java +++ b/src/share/classes/java/lang/invoke/LambdaFormEditor.java @@ -443,8 +443,383 @@ class LambdaFormEditor { buf.insertParameter(0, newBaseAddress); } - buf.endEdit(); - form = buf.lambdaForm(); + form = buf.endEdit(); return putInCache(key, form); } + + LambdaForm addArgumentForm(int pos, BasicType type) { + Transform key = Transform.of(Transform.Kind.ADD_ARG, pos, type.ordinal()); + LambdaForm form = getInCache(key); + if (form != null) { + assert(form.arity == lambdaForm.arity+1); + assert(form.parameterType(pos) == type); + return form; + } + LambdaFormBuffer buf = buffer(); + buf.startEdit(); + + buf.insertParameter(pos, new Name(type)); + + form = buf.endEdit(); + return putInCache(key, form); + } + + LambdaForm dupArgumentForm(int srcPos, int dstPos) { + Transform key = Transform.of(Transform.Kind.DUP_ARG, srcPos, dstPos); + LambdaForm form = getInCache(key); + if (form != null) { + assert(form.arity == lambdaForm.arity-1); + return form; + } + LambdaFormBuffer buf = buffer(); + buf.startEdit(); + + assert(lambdaForm.parameter(srcPos).constraint == null); + assert(lambdaForm.parameter(dstPos).constraint == null); + buf.replaceParameterByCopy(dstPos, srcPos); + + form = buf.endEdit(); + return putInCache(key, form); + } + + LambdaForm spreadArgumentsForm(int pos, Class arrayType, int arrayLength) { + Class elementType = arrayType.getComponentType(); + Class erasedArrayType = arrayType; + if (!elementType.isPrimitive()) + erasedArrayType = Object[].class; + BasicType bt = basicType(elementType); + int elementTypeKey = bt.ordinal(); + if (bt.basicTypeClass() != elementType) { + if (elementType.isPrimitive()) { + elementTypeKey = TYPE_LIMIT + Wrapper.forPrimitiveType(elementType).ordinal(); + } + } + Transform key = Transform.of(Transform.Kind.SPREAD_ARGS, pos, elementTypeKey, arrayLength); + LambdaForm form = getInCache(key); + if (form != null) { + assert(form.arity == lambdaForm.arity - arrayLength + 1); + return form; + } + LambdaFormBuffer buf = buffer(); + buf.startEdit(); + + assert(pos <= MethodType.MAX_JVM_ARITY); + assert(pos + arrayLength <= lambdaForm.arity); + assert(pos > 0); // cannot spread the MH arg itself + + Name spreadParam = new Name(L_TYPE); + Name checkSpread = new Name(MethodHandleImpl.Lazy.NF_checkSpreadArgument, spreadParam, arrayLength); + + // insert the new expressions + int exprPos = lambdaForm.arity(); + buf.insertExpression(exprPos++, checkSpread); + // adjust the arguments + MethodHandle aload = MethodHandles.arrayElementGetter(erasedArrayType); + for (int i = 0; i < arrayLength; i++) { + Name loadArgument = new Name(aload, spreadParam, i); + buf.insertExpression(exprPos + i, loadArgument); + buf.replaceParameterByCopy(pos + i, exprPos + i); + } + buf.insertParameter(pos, spreadParam); + + form = buf.endEdit(); + return putInCache(key, form); + } + + LambdaForm collectArgumentsForm(int pos, MethodType collectorType) { + int collectorArity = collectorType.parameterCount(); + boolean dropResult = (collectorType.returnType() == void.class); + if (collectorArity == 1 && !dropResult) { + return filterArgumentForm(pos, basicType(collectorType.parameterType(0))); + } + BasicType[] newTypes = BasicType.basicTypes(collectorType.parameterList()); + Transform.Kind kind = (dropResult + ? Transform.Kind.COLLECT_ARGS_TO_VOID + : Transform.Kind.COLLECT_ARGS); + if (dropResult && collectorArity == 0) pos = 1; // pure side effect + Transform key = Transform.of(kind, pos, collectorArity, BasicType.basicTypesOrd(newTypes)); + LambdaForm form = getInCache(key); + if (form != null) { + assert(form.arity == lambdaForm.arity - (dropResult ? 0 : 1) + collectorArity); + return form; + } + form = makeArgumentCombinationForm(pos, collectorType, false, dropResult); + 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))); + Transform.Kind kind = Transform.Kind.COLLECT_ARGS_TO_ARRAY; + Transform key = Transform.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(arrayCollector, (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) { + Transform key = Transform.of(Transform.Kind.FILTER_ARG, pos, newType.ordinal()); + LambdaForm form = getInCache(key); + if (form != null) { + assert(form.arity == lambdaForm.arity); + assert(form.parameterType(pos) == newType); + return form; + } + + BasicType oldType = lambdaForm.parameterType(pos); + MethodType filterType = MethodType.methodType(oldType.basicTypeClass(), + newType.basicTypeClass()); + form = makeArgumentCombinationForm(pos, filterType, false, false); + return putInCache(key, form); + } + + private LambdaForm makeArgumentCombinationForm(int pos, + MethodType combinerType, + boolean keepArguments, boolean dropResult) { + LambdaFormBuffer buf = buffer(); + buf.startEdit(); + int combinerArity = combinerType.parameterCount(); + int resultArity = (dropResult ? 0 : 1); + + assert(pos <= MethodType.MAX_JVM_ARITY); + assert(pos + resultArity + (keepArguments ? combinerArity : 0) <= lambdaForm.arity); + assert(pos > 0); // cannot filter the MH arg itself + assert(combinerType == combinerType.basicType()); + assert(combinerType.returnType() != void.class || dropResult); + + BoundMethodHandle.SpeciesData oldData = oldSpeciesData(); + BoundMethodHandle.SpeciesData newData = newSpeciesData(L_TYPE); + + // The newly created LF will run with a different BMH. + // Switch over any pre-existing BMH field references to the new BMH class. + Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values + buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress); + Name newBaseAddress = oldBaseAddress.withConstraint(newData); + buf.renameParameter(0, newBaseAddress); + + Name getCombiner = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress); + Object[] combinerArgs = new Object[1 + combinerArity]; + combinerArgs[0] = getCombiner; + Name[] newParams; + if (keepArguments) { + newParams = new Name[0]; + System.arraycopy(lambdaForm.names, pos + resultArity, + combinerArgs, 1, combinerArity); + } else { + newParams = new Name[combinerArity]; + BasicType[] newTypes = basicTypes(combinerType.parameterList()); + for (int i = 0; i < newTypes.length; i++) { + newParams[i] = new Name(pos + i, newTypes[i]); + } + System.arraycopy(newParams, 0, + combinerArgs, 1, combinerArity); + } + Name callCombiner = new Name(combinerType, combinerArgs); + + // insert the two new expressions + int exprPos = lambdaForm.arity(); + buf.insertExpression(exprPos+0, getCombiner); + buf.insertExpression(exprPos+1, callCombiner); + + // insert new arguments, if needed + int argPos = pos + resultArity; // skip result parameter + for (Name newParam : newParams) { + buf.insertParameter(argPos++, newParam); + } + assert(buf.lastIndexOf(callCombiner) == exprPos+1+newParams.length); + if (!dropResult) { + buf.replaceParameterByCopy(pos, exprPos+1+newParams.length); + } + + return buf.endEdit(); + } + + LambdaForm filterReturnForm(BasicType newType, boolean constantZero) { + Transform.Kind kind = (constantZero ? Transform.Kind.FILTER_RETURN_TO_ZERO : Transform.Kind.FILTER_RETURN); + Transform key = Transform.of(kind, newType.ordinal()); + LambdaForm form = getInCache(key); + if (form != null) { + assert(form.arity == lambdaForm.arity); + assert(form.returnType() == newType); + return form; + } + LambdaFormBuffer buf = buffer(); + buf.startEdit(); + + int insPos = lambdaForm.names.length; + Name callFilter; + if (constantZero) { + // Synthesize a constant zero value for the given type. + if (newType == V_TYPE) + callFilter = null; + else + callFilter = new Name(constantZero(newType)); + } else { + BoundMethodHandle.SpeciesData oldData = oldSpeciesData(); + BoundMethodHandle.SpeciesData newData = newSpeciesData(L_TYPE); + + // The newly created LF will run with a different BMH. + // Switch over any pre-existing BMH field references to the new BMH class. + Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values + buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress); + Name newBaseAddress = oldBaseAddress.withConstraint(newData); + buf.renameParameter(0, newBaseAddress); + + Name getFilter = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress); + buf.insertExpression(insPos++, getFilter); + BasicType oldType = lambdaForm.returnType(); + if (oldType == V_TYPE) { + MethodType filterType = MethodType.methodType(newType.basicTypeClass()); + callFilter = new Name(filterType, getFilter); + } else { + MethodType filterType = MethodType.methodType(newType.basicTypeClass(), oldType.basicTypeClass()); + callFilter = new Name(filterType, getFilter, lambdaForm.names[lambdaForm.result]); + } + } + + if (callFilter != null) + buf.insertExpression(insPos++, callFilter); + buf.setResult(callFilter); + + form = buf.endEdit(); + return putInCache(key, form); + } + + LambdaForm foldArgumentsForm(int foldPos, boolean dropResult, MethodType combinerType) { + int combinerArity = combinerType.parameterCount(); + Transform.Kind kind = (dropResult ? Transform.Kind.FOLD_ARGS_TO_VOID : Transform.Kind.FOLD_ARGS); + Transform key = Transform.of(kind, foldPos, combinerArity); + LambdaForm form = getInCache(key); + if (form != null) { + assert(form.arity == lambdaForm.arity - (kind == Transform.Kind.FOLD_ARGS ? 1 : 0)); + return form; + } + form = makeArgumentCombinationForm(foldPos, combinerType, true, dropResult); + return putInCache(key, form); + } + + LambdaForm permuteArgumentsForm(int skip, int[] reorder) { + assert(skip == 1); // skip only the leading MH argument, names[0] + int length = lambdaForm.names.length; + int outArgs = reorder.length; + int inTypes = 0; + boolean nullPerm = true; + for (int i = 0; i < reorder.length; i++) { + int inArg = reorder[i]; + if (inArg != i) nullPerm = false; + inTypes = Math.max(inTypes, inArg+1); + } + assert(skip + reorder.length == lambdaForm.arity); + if (nullPerm) return lambdaForm; // do not bother to cache + Transform key = Transform.of(Transform.Kind.PERMUTE_ARGS, reorder); + LambdaForm form = getInCache(key); + if (form != null) { + assert(form.arity == skip+inTypes) : form; + return form; + } + + BasicType[] types = new BasicType[inTypes]; + for (int i = 0; i < outArgs; i++) { + int inArg = reorder[i]; + types[inArg] = lambdaForm.names[skip + i].type; + } + assert (skip + outArgs == lambdaForm.arity); + assert (permutedTypesMatch(reorder, types, lambdaForm.names, skip)); + int pos = 0; + while (pos < outArgs && reorder[pos] == pos) { + pos += 1; + } + Name[] names2 = new Name[length - outArgs + inTypes]; + System.arraycopy(lambdaForm.names, 0, names2, 0, skip + pos); + int bodyLength = length - lambdaForm.arity; + System.arraycopy(lambdaForm.names, skip + outArgs, names2, skip + inTypes, bodyLength); + int arity2 = names2.length - bodyLength; + int result2 = lambdaForm.result; + if (result2 >= 0) { + if (result2 < skip + outArgs) { + result2 = reorder[result2 - skip]; + } else { + result2 = result2 - outArgs + inTypes; + } + } + for (int j = pos; j < outArgs; j++) { + Name n = lambdaForm.names[skip + j]; + int i = reorder[j]; + Name n2 = names2[skip + i]; + if (n2 == null) { + names2[skip + i] = n2 = new Name(types[i]); + } else { + assert (n2.type == types[i]); + } + for (int k = arity2; k < names2.length; k++) { + names2[k] = names2[k].replaceName(n, n2); + } + } + for (int i = skip + pos; i < arity2; i++) { + if (names2[i] == null) { + names2[i] = argument(i, types[i - skip]); + } + } + for (int j = lambdaForm.arity; j < lambdaForm.names.length; j++) { + int i = j - lambdaForm.arity + arity2; + Name n = lambdaForm.names[j]; + Name n2 = names2[i]; + if (n != n2) { + for (int k = i + 1; k < names2.length; k++) { + names2[k] = names2[k].replaceName(n, n2); + } + } + } + + form = new LambdaForm(lambdaForm.debugName, arity2, names2, result2); + return putInCache(key, form); + } + + static boolean permutedTypesMatch(int[] reorder, BasicType[] types, Name[] names, int skip) { + for (int i = 0; i < reorder.length; i++) { + assert (names[skip + i].isParam()); + assert (names[skip + i].type == types[reorder[i]]); + } + return true; + } } diff --git a/src/share/classes/java/lang/invoke/MethodHandle.java b/src/share/classes/java/lang/invoke/MethodHandle.java index f4c191f9e6..f0169e73df 100644 --- a/src/share/classes/java/lang/invoke/MethodHandle.java +++ b/src/share/classes/java/lang/invoke/MethodHandle.java @@ -864,9 +864,18 @@ assertEquals("[A, B, C]", (String) caToString2.invokeExact('A', "BC".toCharArray * @see #asCollector */ public MethodHandle asSpreader(Class arrayType, int arrayLength) { - asSpreaderChecks(arrayType, arrayLength); - int spreadArgPos = type.parameterCount() - arrayLength; - return MethodHandleImpl.makeSpreadArguments(this, arrayType, spreadArgPos, arrayLength); + MethodType postSpreadType = asSpreaderChecks(arrayType, arrayLength); + int arity = type().parameterCount(); + int spreadArgPos = arity - arrayLength; + if (USE_LAMBDA_FORM_EDITOR) { + MethodHandle afterSpread = this.asType(postSpreadType); + BoundMethodHandle mh = afterSpread.rebind(); + LambdaForm lform = mh.editor().spreadArgumentsForm(1 + spreadArgPos, arrayType, arrayLength); + MethodType preSpreadType = postSpreadType.replaceParameterTypes(spreadArgPos, arity, arrayType); + return mh.copyWith(preSpreadType, lform); + } else { + return MethodHandleImpl.makeSpreadArguments(this, arrayType, spreadArgPos, arrayLength); + } } /** @@ -986,12 +995,24 @@ assertEquals("[123]", (String) longsToString.invokeExact((long)123)); */ public MethodHandle asCollector(Class arrayType, int arrayLength) { asCollectorChecks(arrayType, arrayLength); - int collectArgPos = type().parameterCount()-1; - MethodHandle target = this; - if (arrayType != type().parameterType(collectArgPos)) - target = MethodHandleImpl.makePairwiseConvert(this, type().changeParameterType(collectArgPos, arrayType), true); - MethodHandle collector = MethodHandleImpl.varargsArray(arrayType, arrayLength); - return MethodHandles.collectArguments(target, collectArgPos, collector); + int collectArgPos = type().parameterCount() - 1; + if (USE_LAMBDA_FORM_EDITOR) { + BoundMethodHandle mh = rebind(); + MethodType resultType = type().asCollectorType(arrayType, 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); + } else { + MethodHandle target = this; + if (arrayType != type().parameterType(collectArgPos)) + target = MethodHandleImpl.makePairwiseConvert(this, type().changeParameterType(collectArgPos, arrayType), true); + MethodHandle collector = MethodHandleImpl.varargsArray(arrayType, arrayLength); + return MethodHandles.collectArguments(target, collectArgPos, collector); + } } /** diff --git a/src/share/classes/java/lang/invoke/MethodHandleImpl.java b/src/share/classes/java/lang/invoke/MethodHandleImpl.java index 5335743c15..026b2cd403 100644 --- a/src/share/classes/java/lang/invoke/MethodHandleImpl.java +++ b/src/share/classes/java/lang/invoke/MethodHandleImpl.java @@ -191,7 +191,11 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; assert(dstType.parameterCount() == target.type().parameterCount()); if (srcType == dstType) return target; - return makePairwiseConvertIndirect(target, srcType, strict, monobox); + if (USE_LAMBDA_FORM_EDITOR) { + return makePairwiseConvertByEditor(target, srcType, strict, monobox); + } else { + return makePairwiseConvertIndirect(target, srcType, strict, monobox); + } } private static int countNonNull(Object[] array) { @@ -202,6 +206,63 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; return count; } + static MethodHandle makePairwiseConvertByEditor(MethodHandle target, MethodType srcType, + boolean strict, boolean monobox) { + Object[] convSpecs = computeValueConversions(srcType, target.type(), strict, monobox); + int convCount = countNonNull(convSpecs); + if (convCount == 0) + return target.viewAsType(srcType, strict); + MethodType basicSrcType = srcType.basicType(); + MethodType midType = target.type().basicType(); + BoundMethodHandle mh = target.rebind(); + // FIXME: Reduce number of bindings when there is more than one Class conversion. + // FIXME: Reduce number of bindings when there are repeated conversions. + for (int i = 0; i < convSpecs.length-1; i++) { + Object convSpec = convSpecs[i]; + if (convSpec == null) continue; + MethodHandle fn; + if (convSpec instanceof Class) { + fn = Lazy.MH_castReference.bindTo(convSpec); + } else { + fn = (MethodHandle) convSpec; + } + Class newType = basicSrcType.parameterType(i); + if (--convCount == 0) + midType = srcType; + else + midType = midType.changeParameterType(i, newType); + LambdaForm form2 = mh.editor().filterArgumentForm(1+i, BasicType.basicType(newType)); + mh = mh.copyWithExtendL(midType, form2, fn); + mh = mh.rebind(); + } + Object convSpec = convSpecs[convSpecs.length-1]; + if (convSpec != null) { + MethodHandle fn; + if (convSpec instanceof Class) { + if (convSpec == void.class) + fn = null; + else + fn = Lazy.MH_castReference.bindTo(convSpec); + } else { + fn = (MethodHandle) convSpec; + } + Class newType = basicSrcType.returnType(); + assert(--convCount == 0); + midType = srcType; + if (fn != null) { + mh = mh.rebind(); // rebind if too complex + LambdaForm form2 = mh.editor().filterReturnForm(BasicType.basicType(newType), false); + mh = mh.copyWithExtendL(midType, form2, fn); + } else { + LambdaForm form2 = mh.editor().filterReturnForm(BasicType.basicType(newType), true); + mh = mh.copyWith(midType, form2); + } + } + assert(convCount == 0); + assert(mh.type().equals(srcType)); + return mh; + } + static MethodHandle makePairwiseConvertIndirect(MethodHandle target, MethodType srcType, boolean strict, boolean monobox) { // Calculate extra arguments (temporaries) required in the names array. diff --git a/src/share/classes/java/lang/invoke/MethodHandleStatics.java b/src/share/classes/java/lang/invoke/MethodHandleStatics.java index a687cac362..c3d9ac12c0 100644 --- a/src/share/classes/java/lang/invoke/MethodHandleStatics.java +++ b/src/share/classes/java/lang/invoke/MethodHandleStatics.java @@ -45,19 +45,21 @@ import sun.misc.Unsafe; static final boolean DUMP_CLASS_FILES; static final boolean TRACE_INTERPRETER; static final boolean TRACE_METHOD_LINKAGE; + static final boolean USE_LAMBDA_FORM_EDITOR; static final int COMPILE_THRESHOLD; static final int PROFILE_LEVEL; static { - final Object[] values = { false, false, false, false, null, null }; + final Object[] values = { false, false, false, false, false, null, null }; AccessController.doPrivileged(new PrivilegedAction() { public Void run() { values[0] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DEBUG_NAMES"); values[1] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DUMP_CLASS_FILES"); values[2] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_INTERPRETER"); values[3] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE"); - values[4] = Integer.getInteger("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD", 30); - values[5] = Integer.getInteger("java.lang.invoke.MethodHandle.PROFILE_LEVEL", 0); + 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); return null; } }); @@ -65,8 +67,9 @@ import sun.misc.Unsafe; DUMP_CLASS_FILES = (Boolean) values[1]; TRACE_INTERPRETER = (Boolean) values[2]; TRACE_METHOD_LINKAGE = (Boolean) values[3]; - COMPILE_THRESHOLD = (Integer) values[4]; - PROFILE_LEVEL = (Integer) values[5]; + USE_LAMBDA_FORM_EDITOR = (Boolean) values[4]; + COMPILE_THRESHOLD = (Integer) values[5]; + PROFILE_LEVEL = (Integer) values[6]; } /** Tell if any of the debugging switches are turned on. diff --git a/src/share/classes/java/lang/invoke/MethodHandles.java b/src/share/classes/java/lang/invoke/MethodHandles.java index 9348525890..68ff317a44 100644 --- a/src/share/classes/java/lang/invoke/MethodHandles.java +++ b/src/share/classes/java/lang/invoke/MethodHandles.java @@ -28,7 +28,6 @@ package java.lang.invoke; import java.lang.reflect.*; import java.util.BitSet; import java.util.List; -import java.util.ArrayList; import java.util.Arrays; import sun.invoke.util.ValueConversions; @@ -2092,23 +2091,86 @@ assert((int)twice.invokeExact(21) == 42); */ public static MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) { - reorder = reorder.clone(); - permuteArgumentChecks(reorder, newType, target.type()); - // first detect dropped arguments and handle them separately - MethodHandle originalTarget = target; - int newArity = newType.parameterCount(); - for (int dropIdx; (dropIdx = findFirstDrop(reorder, newArity)) >= 0; ) { - // dropIdx is missing from reorder; add it in at the end - int oldArity = reorder.length; - target = dropArguments(target, oldArity, newType.parameterType(dropIdx)); - reorder = Arrays.copyOf(reorder, oldArity+1); - reorder[oldArity] = dropIdx; - } - assert(target == originalTarget || permuteArgumentChecks(reorder, newType, target.type())); - // Note: This may cache too many distinct LFs. Consider backing off to varargs code. - BoundMethodHandle result = target.rebind(); - LambdaForm form = result.form.permuteArguments(1, reorder, basicTypes(newType.parameterList())); - return result.copyWith(newType, form); + reorder = reorder.clone(); // get a private copy + MethodType oldType = target.type(); + permuteArgumentChecks(reorder, newType, oldType); + if (USE_LAMBDA_FORM_EDITOR) { + // first detect dropped arguments and handle them separately + int[] originalReorder = reorder; + BoundMethodHandle result = target.rebind(); + LambdaForm form = result.form; + int newArity = newType.parameterCount(); + // Normalize the reordering into a real permutation, + // by removing duplicates and adding dropped elements. + // This somewhat improves lambda form caching, as well + // as simplifying the transform by breaking it up into steps. + for (int ddIdx; (ddIdx = findFirstDupOrDrop(reorder, newArity)) != 0; ) { + if (ddIdx > 0) { + // We found a duplicated entry at reorder[ddIdx]. + // Example: (x,y,z)->asList(x,y,z) + // permuted by [1*,0,1] => (a0,a1)=>asList(a1,a0,a1) + // permuted by [0,1,0*] => (a0,a1)=>asList(a0,a1,a0) + // The starred element corresponds to the argument + // deleted by the dupArgumentForm transform. + int srcPos = ddIdx, dstPos = srcPos, dupVal = reorder[srcPos]; + boolean killFirst = false; + for (int val; (val = reorder[--dstPos]) != dupVal; ) { + // Set killFirst if the dup is larger than an intervening position. + // This will remove at least one inversion from the permutation. + if (dupVal > val) killFirst = true; + } + if (!killFirst) { + srcPos = dstPos; + dstPos = ddIdx; + } + form = form.editor().dupArgumentForm(1 + srcPos, 1 + dstPos); + assert (reorder[srcPos] == reorder[dstPos]); + oldType = oldType.dropParameterTypes(dstPos, dstPos + 1); + // contract the reordering by removing the element at dstPos + int tailPos = dstPos + 1; + System.arraycopy(reorder, tailPos, reorder, dstPos, reorder.length - tailPos); + reorder = Arrays.copyOf(reorder, reorder.length - 1); + } else { + int dropVal = ~ddIdx, insPos = 0; + while (insPos < reorder.length && reorder[insPos] < dropVal) { + // Find first element of reorder larger than dropVal. + // This is where we will insert the dropVal. + insPos += 1; + } + Class ptype = newType.parameterType(dropVal); + form = form.editor().addArgumentForm(1 + insPos, BasicType.basicType(ptype)); + oldType = oldType.insertParameterTypes(insPos, ptype); + // expand the reordering by inserting an element at insPos + int tailPos = insPos + 1; + reorder = Arrays.copyOf(reorder, reorder.length + 1); + System.arraycopy(reorder, insPos, reorder, tailPos, reorder.length - tailPos); + reorder[insPos] = dropVal; + } + assert (permuteArgumentChecks(reorder, newType, oldType)); + } + assert (reorder.length == newArity); // a perfect permutation + // Note: This may cache too many distinct LFs. Consider backing off to varargs code. + form = form.editor().permuteArgumentsForm(1, reorder); + if (newType == result.type() && form == result.internalForm()) + return result; + return result.copyWith(newType, form); + } else { + // first detect dropped arguments and handle them separately + MethodHandle originalTarget = target; + int newArity = newType.parameterCount(); + for (int dropIdx; (dropIdx = findFirstDrop(reorder, newArity)) >= 0; ) { + // dropIdx is missing from reorder; add it in at the end + int oldArity = reorder.length; + target = dropArguments(target, oldArity, newType.parameterType(dropIdx)); + reorder = Arrays.copyOf(reorder, oldArity+1); + reorder[oldArity] = dropIdx; + } + assert(target == originalTarget || permuteArgumentChecks(reorder, newType, target.type())); + // Note: This may cache too many distinct LFs. Consider backing off to varargs code. + BoundMethodHandle result = target.rebind(); + LambdaForm form = result.form.permuteArguments(1, reorder, basicTypes(newType.parameterList())); + return result.copyWith(newType, form); + } } /** Return the first value in [0..newArity-1] that is not present in reorder. */ @@ -2141,6 +2203,52 @@ assert((int)twice.invokeExact(21) == 42); return zeroPos; } + /** + * Return an indication of any duplicate or omission in reorder. + * If the reorder contains a duplicate entry, return the index of the second occurrence. + * Otherwise, return ~(n), for the first n in [0..newArity-1] that is not present in reorder. + * Otherwise, return zero. + * If an element not in [0..newArity-1] is encountered, return reorder.length. + */ + private static int findFirstDupOrDrop(int[] reorder, int newArity) { + final int BIT_LIMIT = 63; // max number of bits in bit mask + if (newArity < BIT_LIMIT) { + long mask = 0; + for (int i = 0; i < reorder.length; i++) { + int arg = reorder[i]; + if (arg >= newArity) return reorder.length; + int bit = 1 << arg; + if ((mask & bit) != 0) + return i; // >0 indicates a dup + mask |= bit; + } + if (mask == (1 << newArity) - 1) { + assert(Long.numberOfTrailingZeros(Long.lowestOneBit(~mask)) == newArity); + return 0; + } + // find first zero + long zeroBit = Long.lowestOneBit(~mask); + int zeroPos = Long.numberOfTrailingZeros(zeroBit); + assert(zeroPos < newArity); + return ~zeroPos; + } else { + // same algorithm, different bit set + BitSet mask = new BitSet(newArity); + for (int i = 0; i < reorder.length; i++) { + int arg = reorder[i]; + if (arg >= newArity) return reorder.length; + if (mask.get(arg)) + return i; // >0 indicates a dup + mask.set(arg); + } + int zeroPos = mask.nextClearBit(0); + if (zeroPos == newArity) { + return 0; + } + return ~zeroPos; + } + } + private static boolean permuteArgumentChecks(int[] reorder, MethodType newType, MethodType oldType) { if (newType.returnType() != oldType.returnType()) throw newIllegalArgumentException("return types do not match", @@ -2372,7 +2480,14 @@ assertEquals("yz", (String) d0.invokeExact(123, "x", "y", "z")); if (dropped == 0) return target; BoundMethodHandle result = target.rebind(); LambdaForm lform = result.form; - lform = lform.addArguments(pos, valueTypes); + if (USE_LAMBDA_FORM_EDITOR) { + int insertFormArg = 1 + pos; + for (Class ptype : valueTypes) { + lform = lform.editor().addArgumentForm(insertFormArg++, BasicType.basicType(ptype)); + } + } else { + lform = lform.addArguments(pos, valueTypes); + } MethodType newType = oldType.insertParameterTypes(pos, valueTypes); result = result.copyWith(newType, lform); return result; @@ -2523,7 +2638,18 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY /*non-public*/ static MethodHandle filterArgument(MethodHandle target, int pos, MethodHandle filter) { filterArgumentChecks(target, pos, filter); - return MethodHandleImpl.makeCollectArguments(target, filter, pos, false); + if (USE_LAMBDA_FORM_EDITOR) { + MethodType targetType = target.type(); + MethodType filterType = filter.type(); + BoundMethodHandle result = target.rebind(); + Class newParamType = filterType.parameterType(0); + LambdaForm lform = result.editor().filterArgumentForm(1 + pos, BasicType.basicType(newParamType)); + MethodType newType = targetType.changeParameterType(pos, newParamType); + result = result.copyWithExtendL(newType, lform, filter); + return result; + } else { + return MethodHandleImpl.makeCollectArguments(target, filter, pos, false); + } } private static void filterArgumentsCheckArity(MethodHandle target, int pos, MethodHandle[] filters) { @@ -2649,12 +2775,36 @@ assertEquals("[top, [[up, down, strange], charm], bottom]", */ public static MethodHandle collectArguments(MethodHandle target, int pos, MethodHandle filter) { + MethodType newType = collectArgumentsChecks(target, pos, filter); + if (USE_LAMBDA_FORM_EDITOR) { + 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()); + return result.copyWithExtendL(newType, lform, filter); + } else { + return MethodHandleImpl.makeCollectArguments(target, filter, pos, false); + } + } + + private static MethodType collectArgumentsChecks(MethodHandle target, int pos, MethodHandle filter) throws RuntimeException { MethodType targetType = target.type(); MethodType filterType = filter.type(); - if (filterType.returnType() != void.class && - filterType.returnType() != targetType.parameterType(pos)) + Class rtype = filterType.returnType(); + List> filterArgs = filterType.parameterList(); + if (rtype == void.class) { + return targetType.insertParameterTypes(pos, filterArgs); + } + if (rtype != targetType.parameterType(pos)) { throw newIllegalArgumentException("target and filter types do not match", targetType, filterType); - return MethodHandleImpl.makeCollectArguments(target, filter, pos, false); + } + return targetType.dropParameterTypes(pos, pos+1).insertParameterTypes(pos, filterArgs); } /** @@ -2719,7 +2869,16 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 MethodType targetType = target.type(); MethodType filterType = filter.type(); filterReturnValueChecks(targetType, filterType); - return MethodHandleImpl.makeCollectArguments(filter, target, 0, false); + if (USE_LAMBDA_FORM_EDITOR) { + BoundMethodHandle result = target.rebind(); + BasicType rtype = BasicType.basicType(filterType.returnType()); + LambdaForm lform = result.editor().filterReturnForm(rtype, false); + MethodType newType = targetType.changeReturnType(filterType.returnType()); + result = result.copyWithExtendL(newType, lform, filter); + return result; + } else { + return MethodHandleImpl.makeCollectArguments(filter, target, 0, false); + } } private static void filterReturnValueChecks(MethodType targetType, MethodType filterType) throws RuntimeException { @@ -2813,7 +2972,19 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum")); MethodType targetType = target.type(); MethodType combinerType = combiner.type(); Class rtype = foldArgumentChecks(foldPos, targetType, combinerType); - return MethodHandleImpl.makeCollectArguments(target, combiner, foldPos, true); + if (USE_LAMBDA_FORM_EDITOR) { + BoundMethodHandle result = target.rebind(); + boolean dropResult = (rtype == void.class); + // Note: This may cache too many distinct LFs. Consider backing off to varargs code. + LambdaForm lform = result.editor().foldArgumentsForm(1 + foldPos, dropResult, combinerType.basicType()); + MethodType newType = targetType; + if (!dropResult) + newType = newType.dropParameterTypes(foldPos, foldPos + 1); + result = result.copyWithExtendL(newType, lform, combiner); + return result; + } else { + return MethodHandleImpl.makeCollectArguments(target, combiner, foldPos, true); + } } private static Class foldArgumentChecks(int foldPos, MethodType targetType, MethodType combinerType) { -- cgit v1.2.3