# HG changeset patch # User redestad # Date 1473153152 -7200 # Tue Sep 06 11:12:32 2016 +0200 # Node ID a1ccd56e4e6e399e84ac6aae17b0d2a5dd4932a2 # Parent 04b6837b1be5d222e660dfb8d09a0ed07248d2f0 imported patch fold_select diff --git a/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java b/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java --- a/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java +++ b/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java @@ -83,7 +83,9 @@ FOLD_ARGS = 11, FOLD_ARGS_TO_VOID = 12, PERMUTE_ARGS = 13, - LOCAL_TYPES = 14; + LOCAL_TYPES = 14, + FOLD_SELECT_ARGS = 15, + FOLD_SELECT_ARGS_TO_VOID = 16; private static final boolean STRESS_TEST = false; // turn on to disable most packing private static final int @@ -695,6 +697,74 @@ return buf.endEdit(); } + + private LambdaForm makeArgumentCombinationForm(int pos, + MethodType combinerType, + int[] argPositions, + boolean keepArguments, + boolean dropResult) { + LambdaFormBuffer buf = buffer(); + buf.startEdit(); + int combinerArity = combinerType.parameterCount(); + assert(combinerArity == argPositions.length); + + 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]; + for (int i = 0; i < combinerArity; i++) { + combinerArgs[i + 1] = lambdaForm.parameter(1 + argPositions[i]); + assert (basicType(combinerType.parameterType(i)) == lambdaForm.parameterType(1 + argPositions[i])); + } + } else { + newParams = new Name[combinerArity]; + for (int i = 0; i < newParams.length; i++) { + newParams[i] = lambdaForm.parameter(1 + argPositions[i]); + assert (basicType(combinerType.parameterType(i)) == lambdaForm.parameterType(1 + argPositions[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) { byte kind = (constantZero ? Transform.FILTER_RETURN_TO_ZERO : Transform.FILTER_RETURN); Transform key = Transform.of(kind, newType.ordinal()); @@ -759,6 +829,21 @@ return putInCache(key, form); } + LambdaForm foldArgumentsForm(int foldPos, boolean dropResult, MethodType combinerType, int ... argPositions) { + byte kind = (dropResult ? Transform.FOLD_SELECT_ARGS_TO_VOID + : Transform.FOLD_SELECT_ARGS); + int[] keyArgs = Arrays.copyOf(argPositions, argPositions.length + 1); + keyArgs[argPositions.length] = foldPos; + Transform key = Transform.of(kind, keyArgs); + LambdaForm form = getInCache(key); + if (form != null) { + assert(form.arity == lambdaForm.arity - (kind == Transform.FOLD_SELECT_ARGS ? 1 : 0)); + return form; + } + form = makeArgumentCombinationForm(foldPos, combinerType, argPositions, 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; diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java --- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java @@ -3942,6 +3942,33 @@ throw misMatchedTypes("target and combiner types", targetType, combinerType); return rtype; } + + private static Class foldArgumentChecks(int foldPos, MethodType targetType, MethodType combinerType, int ... argPos) { + int foldArgs = combinerType.parameterCount(); + if (argPos.length != foldArgs) { + throw newIllegalArgumentException("combiner and argument map must be equal size", combinerType, argPos.length); + } + Class rtype = combinerType.returnType(); + int foldVals = rtype == void.class ? 0 : 1; + boolean ok = true; + for (int i = 0; i < foldArgs; i++) { + int arg = argPos[i]; + if (arg < 0 || arg > targetType.parameterCount()) { + throw newIllegalArgumentException("arg outside of target parameterRange", targetType, arg); + } + if (combinerType.parameterType(i) != targetType.parameterType(arg)) { + throw newIllegalArgumentException("target argument type at position " + arg + + " must match combiner argument type at index " + i + ": " + targetType + + " -> " + combinerType + ", map: " + Arrays.toString(argPos)); + } + } + if (ok && foldVals != 0 && combinerType.returnType() != targetType.parameterType(foldPos)) { + ok = false; + } + if (!ok) + throw misMatchedTypes("target and combiner types", targetType, combinerType); + return rtype; + } /** * Makes a method handle which adapts a target method handle, @@ -4949,7 +4976,29 @@ return result; } - + /** + * + * @param target + * @param pos + * @param combiner + * @param argPositions + * @return + */ + static MethodHandle foldArguments(MethodHandle target, int pos, MethodHandle combiner, int ... argPositions) { + MethodType targetType = target.type(); + MethodType combinerType = combiner.type(); + Class rtype = foldArgumentChecks(pos, targetType, combinerType, argPositions); + BoundMethodHandle result = target.rebind(); + boolean dropResult = rtype == void.class; + LambdaForm lform = result.editor().foldArgumentsForm(1 + pos, dropResult, combinerType.basicType(), argPositions); + MethodType newType = targetType; + if (!dropResult) { + newType = newType.dropParameterTypes(pos, pos + 1); + } + result = result.copyWithExtendL(newType, lform, combiner); + return result; + } + private static void checkLoop0(MethodHandle[][] clauses) { if (clauses == null || clauses.length == 0) { throw newIllegalArgumentException("null or no clauses passed"); diff --git a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java --- a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java +++ b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java @@ -1505,25 +1505,29 @@ // known from the combinators below. We are assembling the string backwards, so "index" is the // *ending* index. for (RecipeElement el : recipe.getElements()) { - MethodHandle prepender; switch (el.getTag()) { - case TAG_CONST: + case TAG_CONST: { Object cnst = el.getValue(); - prepender = MethodHandles.insertArguments(prepender(cnst.getClass()), 3, cnst); + MethodHandle prepender = MethodHandles.insertArguments(prepender(cnst.getClass()), 3, cnst); + mh = MethodHandles.dropArguments(mh, 1, int.class); + mh = MethodHandles.foldArguments(mh, prepender); break; - case TAG_ARG: + } + case TAG_ARG: { int pos = el.getArgPos(); - prepender = selectArgument(prepender(ptypes[pos]), 3, ptypes, pos); + MethodHandle prepender = prepender(ptypes[pos]); + mh = MethodHandles.dropArguments(mh, 1, int.class); + mh = MethodHandles.foldArguments(mh, 0, prepender, 1, 2, 3, 4 + pos); break; + } default: throw new StringConcatException("Unhandled tag: " + el.getTag()); } // Remove "old" index from arguments - mh = MethodHandles.dropArguments(mh, 1, int.class); + //mh = MethodHandles.dropArguments(mh, 1, int.class); // Do the prepend, and put "new" index at index 0 - mh = MethodHandles.foldArguments(mh, prepender); } // Prepare the argument list for prepending. The tree below would instantiate @@ -1567,12 +1571,9 @@ int ac = el.getArgPos(); Class argClass = ptypes[ac]; - MethodHandle lm = selectArgument(lengthMixer(argClass), 1, ptypes, ac); - lm = MethodHandles.dropArguments(lm, 0, byte.class); // (*) - lm = MethodHandles.dropArguments(lm, 2, byte.class); + MethodHandle lm = lengthMixer(argClass); - MethodHandle cm = selectArgument(coderMixer(argClass), 1, ptypes, ac); - cm = MethodHandles.dropArguments(cm, 0, int.class); // (**) + MethodHandle cm = coderMixer(argClass); // Read this bottom up: @@ -1581,11 +1582,11 @@ // 3. Compute "new-index", producing ("new-index", "new-coder", "old-index", "old-coder", ) // Length mixer ignores both "new-coder" and "old-coder" due to dropArguments above (*) - mh = MethodHandles.foldArguments(mh, lm); + mh = MethodHandles.foldArguments(mh, 0, lm, 2, 4 + ac); // 2. Compute "new-coder", producing ("new-coder", "old-index", "old-coder", ) // Coder mixer ignores the "old-index" arg due to dropArguments above (**) - mh = MethodHandles.foldArguments(mh, cm); + mh = MethodHandles.foldArguments(mh, 0, cm, 2, 3 + ac); // 1. The mh shape here is ("old-index", "old-coder", ) break; @@ -1616,18 +1617,6 @@ return perm; } - // Adapts: (...prefix..., parameter[pos])R -> (...prefix..., ...parameters...)R - private static MethodHandle selectArgument(MethodHandle mh, int prefix, Class[] ptypes, int pos) { - if (pos == 0) { - return MethodHandles.dropArguments(mh, prefix + 1, Arrays.copyOfRange(ptypes, 1, ptypes.length)); - } else if (pos == ptypes.length - 1) { - return MethodHandles.dropArguments(mh, prefix, Arrays.copyOf(ptypes, ptypes.length - 1)); - } else { // 0 < pos < ptypes.size() - 1 - MethodHandle t = MethodHandles.dropArguments(mh, prefix, Arrays.copyOf(ptypes, pos)); - return MethodHandles.dropArguments(t, prefix + 1 + pos, Arrays.copyOfRange(ptypes, pos + 1, ptypes.length)); - } - } - @ForceInline private static byte[] newArray(int length, byte coder) { return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, length << coder); # HG changeset patch # User redestad # Date 1473153153 -7200 # Tue Sep 06 11:12:33 2016 +0200 # Node ID 378d1c4904eade39564d430ddbada33c4a9de643 # Parent a1ccd56e4e6e399e84ac6aae17b0d2a5dd4932a2 imported patch noperm diff --git a/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java b/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java --- a/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java +++ b/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java @@ -710,8 +710,7 @@ int resultArity = (dropResult ? 0 : 1); - assert(pos <= MethodType.MAX_JVM_ARITY); - assert(pos + resultArity + (keepArguments ? combinerArity : 0) <= lambdaForm.arity); + assert(pos <= lambdaForm.arity); assert(pos > 0); // cannot filter the MH arg itself assert(combinerType == combinerType.basicType()); assert(combinerType.returnType() != void.class || dropResult); diff --git a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java --- a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java +++ b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java @@ -563,9 +563,8 @@ } if ((lookup.lookupModes() & MethodHandles.Lookup.PRIVATE) == 0) { - throw new StringConcatException(String.format( - "Invalid caller: %s", - lookup.lookupClass().getName())); + throw new StringConcatException("Invalid caller: " + + lookup.lookupClass().getName()); } int cCount = 0; @@ -1495,54 +1494,45 @@ MethodHandle mh; mh = MethodHandles.dropArguments(NEW_STRING, 2, ptypes); - mh = MethodHandles.dropArguments(mh, 0, int.class); + mh = MethodHandles.dropArguments(mh, 1, int.class); // Safety: check that remaining index is zero -- that would mean the storage is completely // overwritten, and no leakage of uninitialized data occurred. - mh = MethodHandles.filterArgument(mh, 0, CHECK_INDEX); + mh = MethodHandles.filterArgument(mh, 1, CHECK_INDEX); - // Mix in prependers. This happens when (int, byte[], byte) = (index, storage, coder) is already + // Mix in prependers. This happens when (byte[], int, byte) = (storage, index, coder) is already // known from the combinators below. We are assembling the string backwards, so "index" is the // *ending* index. for (RecipeElement el : recipe.getElements()) { + // Do the prepend, and put "new" index at index 1 + mh = MethodHandles.dropArguments(mh, 2, int.class); switch (el.getTag()) { case TAG_CONST: { Object cnst = el.getValue(); MethodHandle prepender = MethodHandles.insertArguments(prepender(cnst.getClass()), 3, cnst); - mh = MethodHandles.dropArguments(mh, 1, int.class); - mh = MethodHandles.foldArguments(mh, prepender); + mh = MethodHandles.foldArguments(mh, 1, prepender, + 2, 0, 3 // index, storage, coder + ); break; } case TAG_ARG: { int pos = el.getArgPos(); MethodHandle prepender = prepender(ptypes[pos]); - mh = MethodHandles.dropArguments(mh, 1, int.class); - mh = MethodHandles.foldArguments(mh, 0, prepender, 1, 2, 3, 4 + pos); + mh = MethodHandles.foldArguments(mh, 1, prepender, + 2, 0, 3, // index, storage, coder + 4 + pos // selected argument + ); break; } default: throw new StringConcatException("Unhandled tag: " + el.getTag()); } - - // Remove "old" index from arguments - //mh = MethodHandles.dropArguments(mh, 1, int.class); - - // Do the prepend, and put "new" index at index 0 } - // Prepare the argument list for prepending. The tree below would instantiate - // the storage byte[] into argument 0, so we need to swap "storage" and "index". - // The index at this point equals to "size", and resides at argument 1. - { - MethodType nmt = mh.type() - .changeParameterType(0, byte[].class) - .changeParameterType(1, int.class); - mh = MethodHandles.permuteArguments(mh, nmt, swap10(nmt.parameterCount())); - } - - // Fold in byte[] instantiation at argument 0. - MethodHandle combiner = MethodHandles.dropArguments(NEW_ARRAY, 2, ptypes); - mh = MethodHandles.foldArguments(mh, combiner); + // Fold in byte[] instantiation at argument 0 + mh = MethodHandles.foldArguments(mh, 0, NEW_ARRAY, + 1, 2 // index, coder + ); // Start combining length and coder mixers. // @@ -1572,7 +1562,6 @@ Class argClass = ptypes[ac]; MethodHandle lm = lengthMixer(argClass); - MethodHandle cm = coderMixer(argClass); // Read this bottom up: @@ -1581,12 +1570,18 @@ mh = MethodHandles.dropArguments(mh, 2, int.class, byte.class); // 3. Compute "new-index", producing ("new-index", "new-coder", "old-index", "old-coder", ) - // Length mixer ignores both "new-coder" and "old-coder" due to dropArguments above (*) - mh = MethodHandles.foldArguments(mh, 0, lm, 2, 4 + ac); + // Length mixer needs old index, plus the appropriate argument + mh = MethodHandles.foldArguments(mh, 0, lm, + 2, // old-index + 4 + ac // selected argument + ); // 2. Compute "new-coder", producing ("new-coder", "old-index", "old-coder", ) - // Coder mixer ignores the "old-index" arg due to dropArguments above (**) - mh = MethodHandles.foldArguments(mh, 0, cm, 2, 3 + ac); + // Coder mixer needs old coder, plus the appropriate argument. + mh = MethodHandles.foldArguments(mh, 0, cm, + 2, // old-coder + 3 + ac // selected argument + ); // 1. The mh shape here is ("old-index", "old-coder", ) break; @@ -1607,16 +1602,6 @@ return mh; } - private static int[] swap10(int count) { - int[] perm = new int[count]; - perm[0] = 1; - perm[1] = 0; - for (int i = 2; i < count; i++) { - perm[i] = i; - } - return perm; - } - @ForceInline private static byte[] newArray(int length, byte coder) { return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, length << coder);