8212726: Replace some use of drop- and foldArguments with filtering argument combinator in StringConcatFactory
authorredestad
Tue, 23 Oct 2018 11:03:51 +0200
changeset 52226 b4b932c6001f
parent 52225 3c12f0c0a68c
child 52227 eadd0abbfdf4
child 52266 7530494ed51d
8212726: Replace some use of drop- and foldArguments with filtering argument combinator in StringConcatFactory Reviewed-by: jlaskey, vlivanov
src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java
src/java.base/share/classes/java/lang/invoke/MethodHandles.java
src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java
--- a/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java	Tue Oct 23 10:55:59 2018 +0200
+++ b/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java	Tue Oct 23 11:03:51 2018 +0200
@@ -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.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -85,7 +85,8 @@
                 PERMUTE_ARGS = 13,
                 LOCAL_TYPES = 14,
                 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 int
@@ -730,21 +731,23 @@
         Name getCombiner = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress);
         Object[] combinerArgs = new Object[1 + combinerArity];
         combinerArgs[0] = getCombiner;
-        Name[] newParams;
+        Name newParam = null;
         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]);
+            newParam = new Name(pos, BasicType.basicType(combinerType.returnType()));
+            for (int i = 0; i < combinerArity; 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]));
             }
-            System.arraycopy(newParams, 0,
-                             combinerArgs, 1, combinerArity);
         }
         Name callCombiner = new Name(combinerType, combinerArgs);
 
@@ -755,12 +758,13 @@
 
         // insert new arguments, if needed
         int argPos = pos + resultArity;  // skip result parameter
-        for (Name newParam : newParams) {
+        if (newParam != null) {
             buf.insertParameter(argPos++, newParam);
+            exprPos++;
         }
-        assert(buf.lastIndexOf(callCombiner) == exprPos+1+newParams.length);
+        assert(buf.lastIndexOf(callCombiner) == exprPos+1);
         if (!dropResult) {
-            buf.replaceParameterByCopy(pos, exprPos+1+newParams.length);
+            buf.replaceParameterByCopy(pos, exprPos+1);
         }
 
         return buf.endEdit();
@@ -845,6 +849,20 @@
         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) {
         assert(skip == 1);  // skip only the leading MH argument, names[0]
         int length = lambdaForm.names.length;
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Tue Oct 23 10:55:59 2018 +0200
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Tue Oct 23 11:03:51 2018 +0200
@@ -4316,28 +4316,6 @@
         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) {
         int foldArgs   = combinerType.parameterCount();
         Class<?> rtype = combinerType.returnType();
@@ -4359,15 +4337,78 @@
         return rtype;
     }
 
-    private static Class<?> foldArgumentChecks(int foldPos, MethodType targetType, MethodType combinerType, int ... argPos) {
-        int foldArgs = combinerType.parameterCount();
-        if (argPos.length != foldArgs) {
+    /**
+     * Adapts a target method handle by pre-processing some of its arguments, then calling the target with the result
+     * 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);
         }
         Class<?> rtype = combinerType.returnType();
-        int foldVals = rtype == void.class ? 0 : 1;
-        boolean ok = true;
-        for (int i = 0; i < foldArgs; i++) {
+
+        for (int i = 0; i < combinerArgs; i++) {
             int arg = argPos[i];
             if (arg < 0 || arg > targetType.parameterCount()) {
                 throw newIllegalArgumentException("arg outside of target parameterRange", targetType, arg);
@@ -4378,11 +4419,9 @@
                         + " -> " + combinerType + ", map: " + Arrays.toString(argPos));
             }
         }
-        if (ok && foldVals != 0 && combinerType.returnType() != targetType.parameterType(foldPos)) {
-            ok = false;
+        if (filter && combinerType.returnType() != targetType.parameterType(position)) {
+            throw misMatchedTypes("target and combiner types", targetType, combinerType);
         }
-        if (!ok)
-            throw misMatchedTypes("target and combiner types", targetType, combinerType);
         return rtype;
     }
 
--- a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java	Tue Oct 23 10:55:59 2018 +0200
+++ b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java	Tue Oct 23 11:03:51 2018 +0200
@@ -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.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -1533,21 +1533,20 @@
             // *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: {
                         MethodHandle prepender = MethodHandles.insertArguments(prepender(String.class), 3, el.getValue());
-                        mh = MethodHandles.foldArguments(mh, 1, prepender,
-                                2, 0, 3 // index, storage, coder
+                        mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepender,
+                                1, 0, 2 // index, storage, coder
                         );
                         break;
                     }
                     case TAG_ARG: {
                         int pos = el.getArgPos();
                         MethodHandle prepender = prepender(ptypes[pos]);
-                        mh = MethodHandles.foldArguments(mh, 1, prepender,
-                                2, 0, 3, // index, storage, coder
-                                4 + pos  // selected argument
+                        mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, prepender,
+                                1, 0, 2, // index, storage, coder
+                                3 + pos  // selected argument
                         );
                         break;
                     }
@@ -1557,7 +1556,7 @@
             }
 
             // 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
             );
 
@@ -1572,7 +1571,7 @@
             // 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.
             //
-            // 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>)
             byte initialCoder = INITIAL_CODER;
             int initialLen = 0;    // initial length, in characters
@@ -1589,44 +1588,27 @@
                         Class<?> argClass = ptypes[ac];
                         MethodHandle lm = lengthMixer(argClass);
 
-                        // Read these bottom up:
-
                         if (argClass.isPrimitive() && argClass != char.class) {
-
-                            // 3. Drop old index, producing ("new-index", "coder", <args>)
-                            mh = MethodHandles.dropArguments(mh, 1, int.class);
-
-                            // 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
+                            // Compute new "index" in-place using old value plus the appropriate argument.
+                            mh = MethodHandles.filterArgumentsWithCombiner(mh, 0, lm,
+                                    0, // old-index
+                                    2 + 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 {
                             MethodHandle cm = coderMixer(argClass);
 
-                            // 4. Drop old index and coder, producing ("new-index", "new-coder", <args>)
-                            mh = MethodHandles.dropArguments(mh, 2, int.class, byte.class);
-
-                            // 3. Compute "new-index", producing ("new-index", "new-coder", "old-index", "old-coder", <args>)
-                            //    Length mixer needs old index, plus the appropriate argument
-                            mh = MethodHandles.foldArguments(mh, 0, lm,
-                                    2, // old-index
-                                    4 + ac // selected argument
+                            // Compute new "index" in-place using old value plus the appropriate argument.
+                            mh = MethodHandles.filterArgumentsWithCombiner(mh, 0, lm,
+                                    0, // old-index
+                                    2 + ac // selected argument
                             );
 
-                            // 2. Compute "new-coder", producing ("new-coder", "old-index", "old-coder", <args>)
-                            //    Coder mixer needs old coder, plus the appropriate argument.
-                            mh = MethodHandles.foldArguments(mh, 0, cm,
-                                    2, // old-coder
-                                    3 + ac // selected argument
+                            // Compute new "coder" in-place using old value plus the appropriate argument.
+                            mh = MethodHandles.filterArgumentsWithCombiner(mh, 1, cm,
+                                    1, // old-coder
+                                    2 + ac // selected argument
                             );
-
-                            // 1. The mh shape here is ("old-index", "old-coder", <args>)
                         }
 
                         break;