8212726: Replace some use of drop- and foldArguments with filtering argument combinator in StringConcatFactory

Reviewed-by: jlaskey, vlivanov
This commit is contained in:
Claes Redestad 2018-10-23 11:03:51 +02:00
parent a633b90085
commit 06a2bb5274
3 changed files with 121 additions and 82 deletions

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -85,7 +85,8 @@ class LambdaFormEditor {
PERMUTE_ARGS = 13, PERMUTE_ARGS = 13,
LOCAL_TYPES = 14, LOCAL_TYPES = 14,
FOLD_SELECT_ARGS = 15, FOLD_SELECT_ARGS = 15,
FOLD_SELECT_ARGS_TO_VOID = 16; FOLD_SELECT_ARGS_TO_VOID = 16,
FILTER_SELECT_ARGS = 17;
private static final boolean STRESS_TEST = false; // turn on to disable most packing private static final boolean STRESS_TEST = false; // turn on to disable most packing
private static final int private static final int
@ -730,21 +731,23 @@ class LambdaFormEditor {
Name getCombiner = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress); Name getCombiner = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress);
Object[] combinerArgs = new Object[1 + combinerArity]; Object[] combinerArgs = new Object[1 + combinerArity];
combinerArgs[0] = getCombiner; combinerArgs[0] = getCombiner;
Name[] newParams; Name newParam = null;
if (keepArguments) { if (keepArguments) {
newParams = new Name[0];
for (int i = 0; i < combinerArity; i++) { for (int i = 0; i < combinerArity; i++) {
combinerArgs[i + 1] = lambdaForm.parameter(1 + argPositions[i]); combinerArgs[i + 1] = lambdaForm.parameter(1 + argPositions[i]);
assert (basicType(combinerType.parameterType(i)) == lambdaForm.parameterType(1 + argPositions[i])); assert (basicType(combinerType.parameterType(i)) == lambdaForm.parameterType(1 + argPositions[i]));
} }
} else { } else {
newParams = new Name[combinerArity]; newParam = new Name(pos, BasicType.basicType(combinerType.returnType()));
for (int i = 0; i < newParams.length; i++) { for (int i = 0; i < combinerArity; i++) {
newParams[i] = lambdaForm.parameter(1 + argPositions[i]); int argPos = 1 + argPositions[i];
if (argPos == pos) {
combinerArgs[i + 1] = newParam;
} else {
combinerArgs[i + 1] = lambdaForm.parameter(argPos);
}
assert (basicType(combinerType.parameterType(i)) == lambdaForm.parameterType(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); Name callCombiner = new Name(combinerType, combinerArgs);
@ -755,12 +758,13 @@ class LambdaFormEditor {
// insert new arguments, if needed // insert new arguments, if needed
int argPos = pos + resultArity; // skip result parameter int argPos = pos + resultArity; // skip result parameter
for (Name newParam : newParams) { if (newParam != null) {
buf.insertParameter(argPos++, newParam); buf.insertParameter(argPos++, newParam);
exprPos++;
} }
assert(buf.lastIndexOf(callCombiner) == exprPos+1+newParams.length); assert(buf.lastIndexOf(callCombiner) == exprPos+1);
if (!dropResult) { if (!dropResult) {
buf.replaceParameterByCopy(pos, exprPos+1+newParams.length); buf.replaceParameterByCopy(pos, exprPos+1);
} }
return buf.endEdit(); return buf.endEdit();
@ -845,6 +849,20 @@ class LambdaFormEditor {
return putInCache(key, form); return putInCache(key, form);
} }
LambdaForm filterArgumentsForm(int filterPos, MethodType combinerType, int ... argPositions) {
byte kind = Transform.FILTER_SELECT_ARGS;
int[] keyArgs = Arrays.copyOf(argPositions, argPositions.length + 1);
keyArgs[argPositions.length] = filterPos;
Transform key = Transform.of(kind, keyArgs);
LambdaForm form = getInCache(key);
if (form != null) {
assert(form.arity == lambdaForm.arity);
return form;
}
form = makeArgumentCombinationForm(filterPos, combinerType, argPositions, false, false);
return putInCache(key, form);
}
LambdaForm permuteArgumentsForm(int skip, int[] reorder) { LambdaForm permuteArgumentsForm(int skip, int[] reorder) {
assert(skip == 1); // skip only the leading MH argument, names[0] assert(skip == 1); // skip only the leading MH argument, names[0]
int length = lambdaForm.names.length; int length = lambdaForm.names.length;

View file

@ -4316,28 +4316,6 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
return result; return result;
} }
/**
* As {@see foldArguments(MethodHandle, int, MethodHandle)}, but with the
* added capability of selecting the arguments from the targets parameters
* to call the combiner with. This allows us to avoid some simple cases of
* permutations and padding the combiner with dropArguments to select the
* right argument, which may ultimately produce fewer intermediaries.
*/
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 Class<?> foldArgumentChecks(int foldPos, MethodType targetType, MethodType combinerType) { private static Class<?> foldArgumentChecks(int foldPos, MethodType targetType, MethodType combinerType) {
int foldArgs = combinerType.parameterCount(); int foldArgs = combinerType.parameterCount();
Class<?> rtype = combinerType.returnType(); Class<?> rtype = combinerType.returnType();
@ -4359,15 +4337,78 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
return rtype; return rtype;
} }
private static Class<?> foldArgumentChecks(int foldPos, MethodType targetType, MethodType combinerType, int ... argPos) { /**
int foldArgs = combinerType.parameterCount(); * Adapts a target method handle by pre-processing some of its arguments, then calling the target with the result
if (argPos.length != foldArgs) { * of the pre-processing replacing the argument at the given position.
*
* @param target the method handle to invoke after arguments are combined
* @param position the position at which to start folding and at which to insert the folding result; if this is {@code
* 0}, the effect is the same as for {@link #foldArguments(MethodHandle, MethodHandle)}.
* @param combiner method handle to call initially on the incoming arguments
* @param argPositions indexes of the target to pick arguments sent to the combiner from
* @return method handle which incorporates the specified argument folding logic
* @throws NullPointerException if either argument is null
* @throws IllegalArgumentException if either of the following two conditions holds:
* (1) {@code combiner}'s return type is not the same as the argument type at position
* {@code pos} of the target signature;
* (2) the {@code N} argument types at positions {@code argPositions[1...N]} of the target signature are
* not identical with the argument types of {@code combiner}.
*/
/*non-public*/ static MethodHandle filterArgumentsWithCombiner(MethodHandle target, int position, MethodHandle combiner, int ... argPositions) {
return argumentsWithCombiner(true, target, position, combiner, argPositions);
}
/**
* Adapts a target method handle by pre-processing some of its arguments, calling the target with the result of
* the pre-processing inserted into the original sequence of arguments at the given position.
*
* @param target the method handle to invoke after arguments are combined
* @param position the position at which to start folding and at which to insert the folding result; if this is {@code
* 0}, the effect is the same as for {@link #foldArguments(MethodHandle, MethodHandle)}.
* @param combiner method handle to call initially on the incoming arguments
* @param argPositions indexes of the target to pick arguments sent to the combiner from
* @return method handle which incorporates the specified argument folding logic
* @throws NullPointerException if either argument is null
* @throws IllegalArgumentException if either of the following two conditions holds:
* (1) {@code combiner}'s return type is non-{@code void} and not the same as the argument type at position
* {@code pos} of the target signature;
* (2) the {@code N} argument types at positions {@code argPositions[1...N]} of the target signature
* (skipping {@code position} where the {@code combiner}'s return will be folded in) are not identical
* with the argument types of {@code combiner}.
*/
/*non-public*/ static MethodHandle foldArgumentsWithCombiner(MethodHandle target, int position, MethodHandle combiner, int ... argPositions) {
return argumentsWithCombiner(false, target, position, combiner, argPositions);
}
private static MethodHandle argumentsWithCombiner(boolean filter, MethodHandle target, int position, MethodHandle combiner, int ... argPositions) {
MethodType targetType = target.type();
MethodType combinerType = combiner.type();
Class<?> rtype = argumentsWithCombinerChecks(position, filter, targetType, combinerType, argPositions);
BoundMethodHandle result = target.rebind();
MethodType newType = targetType;
LambdaForm lform;
if (filter) {
lform = result.editor().filterArgumentsForm(1 + position, combinerType.basicType(), argPositions);
} else {
boolean dropResult = rtype == void.class;
lform = result.editor().foldArgumentsForm(1 + position, dropResult, combinerType.basicType(), argPositions);
if (!dropResult) {
newType = newType.dropParameterTypes(position, position + 1);
}
}
result = result.copyWithExtendL(newType, lform, combiner);
return result;
}
private static Class<?> argumentsWithCombinerChecks(int position, boolean filter, MethodType targetType, MethodType combinerType, int ... argPos) {
int combinerArgs = combinerType.parameterCount();
if (argPos.length != combinerArgs) {
throw newIllegalArgumentException("combiner and argument map must be equal size", combinerType, argPos.length); throw newIllegalArgumentException("combiner and argument map must be equal size", combinerType, argPos.length);
} }
Class<?> rtype = combinerType.returnType(); Class<?> rtype = combinerType.returnType();
int foldVals = rtype == void.class ? 0 : 1;
boolean ok = true; for (int i = 0; i < combinerArgs; i++) {
for (int i = 0; i < foldArgs; i++) {
int arg = argPos[i]; int arg = argPos[i];
if (arg < 0 || arg > targetType.parameterCount()) { if (arg < 0 || arg > targetType.parameterCount()) {
throw newIllegalArgumentException("arg outside of target parameterRange", targetType, arg); throw newIllegalArgumentException("arg outside of target parameterRange", targetType, arg);
@ -4378,11 +4419,9 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
+ " -> " + combinerType + ", map: " + Arrays.toString(argPos)); + " -> " + combinerType + ", map: " + Arrays.toString(argPos));
} }
} }
if (ok && foldVals != 0 && combinerType.returnType() != targetType.parameterType(foldPos)) { if (filter && combinerType.returnType() != targetType.parameterType(position)) {
ok = false;
}
if (!ok)
throw misMatchedTypes("target and combiner types", targetType, combinerType); throw misMatchedTypes("target and combiner types", targetType, combinerType);
}
return rtype; return rtype;
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -1533,21 +1533,20 @@ public final class StringConcatFactory {
// *ending* index. // *ending* index.
for (RecipeElement el : recipe.getElements()) { for (RecipeElement el : recipe.getElements()) {
// Do the prepend, and put "new" index at index 1 // Do the prepend, and put "new" index at index 1
mh = MethodHandles.dropArguments(mh, 2, int.class);
switch (el.getTag()) { switch (el.getTag()) {
case TAG_CONST: { case TAG_CONST: {
MethodHandle prepender = MethodHandles.insertArguments(prepender(String.class), 3, el.getValue()); MethodHandle prepender = MethodHandles.insertArguments(prepender(String.class), 3, el.getValue());
mh = MethodHandles.foldArguments(mh, 1, prepender, mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepender,
2, 0, 3 // index, storage, coder 1, 0, 2 // index, storage, coder
); );
break; break;
} }
case TAG_ARG: { case TAG_ARG: {
int pos = el.getArgPos(); int pos = el.getArgPos();
MethodHandle prepender = prepender(ptypes[pos]); MethodHandle prepender = prepender(ptypes[pos]);
mh = MethodHandles.foldArguments(mh, 1, prepender, mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepender,
2, 0, 3, // index, storage, coder 1, 0, 2, // index, storage, coder
4 + pos // selected argument 3 + pos // selected argument
); );
break; break;
} }
@ -1557,7 +1556,7 @@ public final class StringConcatFactory {
} }
// Fold in byte[] instantiation at argument 0 // Fold in byte[] instantiation at argument 0
mh = MethodHandles.foldArguments(mh, 0, NEW_ARRAY, mh = MethodHandles.foldArgumentsWithCombiner(mh, 0, NEW_ARRAY,
1, 2 // index, coder 1, 2 // index, coder
); );
@ -1572,7 +1571,7 @@ public final class StringConcatFactory {
// and deduce the coder from there. Arguments would be either converted to Strings // and deduce the coder from there. Arguments would be either converted to Strings
// during the initial filtering, or handled by primitive specializations in CODER_MIXERS. // during the initial filtering, or handled by primitive specializations in CODER_MIXERS.
// //
// The method handle shape after all length and coder mixers is: // The method handle shape before and after all length and coder mixers is:
// (int, byte, <args>)String = ("index", "coder", <args>) // (int, byte, <args>)String = ("index", "coder", <args>)
byte initialCoder = INITIAL_CODER; byte initialCoder = INITIAL_CODER;
int initialLen = 0; // initial length, in characters int initialLen = 0; // initial length, in characters
@ -1589,44 +1588,27 @@ public final class StringConcatFactory {
Class<?> argClass = ptypes[ac]; Class<?> argClass = ptypes[ac];
MethodHandle lm = lengthMixer(argClass); MethodHandle lm = lengthMixer(argClass);
// Read these bottom up:
if (argClass.isPrimitive() && argClass != char.class) { if (argClass.isPrimitive() && argClass != char.class) {
// Compute new "index" in-place using old value plus the appropriate argument.
// 3. Drop old index, producing ("new-index", "coder", <args>) mh = MethodHandles.filterArgumentsWithCombiner(mh, 0, lm,
mh = MethodHandles.dropArguments(mh, 1, int.class); 0, // old-index
2 + ac // selected argument
// 2. Compute "new-index", producing ("new-index", "old-index", "coder", <args>)
// Length mixer needs old index, plus the appropriate argument
mh = MethodHandles.foldArguments(mh, 0, lm,
1, // old-index
3 + ac // selected argument
); );
// 1. The mh shape here is ("old-index", "coder", <args>); we don't need to recalculate
// the coder for non-char primitive arguments
} else { } else {
MethodHandle cm = coderMixer(argClass); MethodHandle cm = coderMixer(argClass);
// 4. Drop old index and coder, producing ("new-index", "new-coder", <args>) // Compute new "index" in-place using old value plus the appropriate argument.
mh = MethodHandles.dropArguments(mh, 2, int.class, byte.class); mh = MethodHandles.filterArgumentsWithCombiner(mh, 0, lm,
0, // old-index
// 3. Compute "new-index", producing ("new-index", "new-coder", "old-index", "old-coder", <args>) 2 + ac // selected argument
// 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", <args>) // Compute new "coder" in-place using old value plus the appropriate argument.
// Coder mixer needs old coder, plus the appropriate argument. mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, cm,
mh = MethodHandles.foldArguments(mh, 0, cm, 1, // old-coder
2, // old-coder 2 + ac // selected argument
3 + ac // selected argument
); );
// 1. The mh shape here is ("old-index", "old-coder", <args>)
} }
break; break;