8050884: Intrinsify ValueConversions.identity() functions
authorvlivanov
Wed, 10 Sep 2014 19:19:50 +0400
changeset 26475 acf0c98309d3
parent 26474 655d08549e43
child 26476 2b814465aaa3
8050884: Intrinsify ValueConversions.identity() functions Reviewed-by: vlivanov, psandoz Contributed-by: john.r.rose@oracle.com
jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java
jdk/src/java.base/share/classes/sun/invoke/util/ValueConversions.java
jdk/test/sun/invoke/util/ValueConversionsTest.java
--- a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Wed Sep 10 19:19:50 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Wed Sep 10 19:19:50 2014 +0400
@@ -664,6 +664,10 @@
                 case ARRAY_STORE:
                     emitArrayStore(name);
                     continue;
+                case IDENTITY:
+                    assert(name.arguments.length == 1);
+                    emitPushArguments(name);
+                    continue;
                 case NONE:
                     // no intrinsic associated
                     break;
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Sep 10 19:19:50 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Sep 10 19:19:50 2014 +0400
@@ -303,14 +303,6 @@
         return new ClassCastException("Cannot cast " + obj.getClass().getName() + " to " + t.getName());
     }
 
-    static MethodHandle makeReferenceIdentity(Class<?> refType) {
-        MethodType lambdaType = MethodType.genericMethodType(1).invokerType();
-        Name[] names = arguments(1, lambdaType);
-        names[names.length - 1] = new Name(ValueConversions.identity(), names[1]);
-        LambdaForm form = new LambdaForm("identity", lambdaType.parameterCount(), names);
-        return SimpleMethodHandle.make(MethodType.methodType(refType, refType), form);
-    }
-
     static Object[] computeValueConversions(MethodType srcType, MethodType dstType,
                                             boolean strict, boolean monobox) {
         final int INARG_COUNT = srcType.parameterCount();
@@ -1061,6 +1053,7 @@
         NEW_ARRAY,
         ARRAY_LOAD,
         ARRAY_STORE,
+        IDENTITY,
         NONE // no intrinsic associated
     }
 
@@ -1098,6 +1091,16 @@
             return super.internalProperties() +
                     "\n& Intrinsic="+intrinsicName;
         }
+
+        @Override
+        public MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
+            if (intrinsicName == Intrinsic.IDENTITY) {
+                MethodType resultType = type().asCollectorType(arrayType, arrayLength);
+                MethodHandle newArray = MethodHandleImpl.varargsArray(arrayType, arrayLength);
+                return newArray.asType(resultType);
+            }
+            return super.asCollector(arrayType, arrayLength);
+        }
     }
 
     static MethodHandle makeIntrinsic(MethodHandle target, Intrinsic intrinsicName) {
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Wed Sep 10 19:19:50 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Wed Sep 10 19:19:50 2014 +0400
@@ -41,6 +41,7 @@
 import java.lang.invoke.LambdaForm.BasicType;
 import static java.lang.invoke.LambdaForm.BasicType.*;
 import static java.lang.invoke.MethodHandleStatics.*;
+import static java.lang.invoke.MethodHandleImpl.Intrinsic;
 import static java.lang.invoke.MethodHandleNatives.Constants.*;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -2198,14 +2199,29 @@
      */
     public static
     MethodHandle identity(Class<?> type) {
-        if (type == void.class)
-            throw newIllegalArgumentException("void type");
-        else if (type == Object.class)
-            return ValueConversions.identity();
-        else if (type.isPrimitive())
-            return ValueConversions.identity(Wrapper.forPrimitiveType(type));
-        else
-            return MethodHandleImpl.makeReferenceIdentity(type);
+        Wrapper btw = (type.isPrimitive() ? Wrapper.forPrimitiveType(type) : Wrapper.OBJECT);
+        int pos = btw.ordinal();
+        MethodHandle ident = IDENTITY_MHS[pos];
+        if (ident == null) {
+            ident = setCachedMethodHandle(IDENTITY_MHS, pos, makeIdentity(btw.primitiveType()));
+        }
+        if (ident.type().returnType() == type)
+            return ident;
+        // something like identity(Foo.class); do not bother to intern these
+        assert(btw == Wrapper.OBJECT);
+        return makeIdentity(type);
+    }
+    private static final MethodHandle[] IDENTITY_MHS = new MethodHandle[Wrapper.values().length];
+    private static MethodHandle makeIdentity(Class<?> ptype) {
+        MethodType mtype = MethodType.methodType(ptype, ptype);
+        LambdaForm lform = LambdaForm.identityForm(BasicType.basicType(ptype));
+        return MethodHandleImpl.makeIntrinsic(mtype, lform, Intrinsic.IDENTITY);
+    }
+    synchronized private static MethodHandle setCachedMethodHandle(MethodHandle[] cache, int pos, MethodHandle value) {
+        // Simulate a CAS, to avoid racy duplication of results.
+        MethodHandle prev = cache[pos];
+        if (prev != null) return prev;
+        return cache[pos] = value;
     }
 
     /**
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java	Wed Sep 10 19:19:50 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java	Wed Sep 10 19:19:50 2014 +0400
@@ -510,6 +510,32 @@
         return ptype;
     }
 
+    /** Delete the last parameter type and replace it with arrayLength copies of the component type of arrayType.
+     * @param arrayType any array type
+     * @param arrayLength the number of parameter types to insert
+     * @return the resulting type
+     */
+    /*non-public*/ MethodType asCollectorType(Class<?> arrayType, int arrayLength) {
+        assert(parameterCount() >= 1);
+        assert(lastParameterType().isAssignableFrom(arrayType));
+        MethodType res;
+        if (arrayType == Object[].class) {
+            res = genericMethodType(arrayLength);
+            if (rtype != Object.class) {
+                res = res.changeReturnType(rtype);
+            }
+        } else {
+            Class<?> elemType = arrayType.getComponentType();
+            assert(elemType != null);
+            res = methodType(rtype, Collections.nCopies(arrayLength, elemType));
+        }
+        if (ptypes.length == 1) {
+            return res;
+        } else {
+            return res.insertParameterTypes(0, parameterList().subList(0, ptypes.length-1));
+        }
+    }
+
     /**
      * Finds or creates a method type with some parameter types omitted.
      * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
--- a/jdk/src/java.base/share/classes/sun/invoke/util/ValueConversions.java	Wed Sep 10 19:19:50 2014 +0400
+++ b/jdk/src/java.base/share/classes/sun/invoke/util/ValueConversions.java	Wed Sep 10 19:19:50 2014 +0400
@@ -395,69 +395,11 @@
         throw new IllegalArgumentException("cannot find zero constant for " + wrap);
     }
 
-    /// Converting references to references.
-
-    /**
-     * Identity function.
-     * @param x an arbitrary reference value
-     * @return the same value x
-     */
-    static <T> T identity(T x) {
-        return x;
-    }
-
-    static <T> T[] identity(T[] x) {
-        return x;
-    }
-
-    /**
-     * Identity function on ints.
-     * @param x an arbitrary int value
-     * @return the same value x
-     */
-    static int identity(int x) {
-        return x;
-    }
-
-    static byte identity(byte x) {
-        return x;
-    }
-
-    static short identity(short x) {
-        return x;
-    }
-
-    static boolean identity(boolean x) {
-        return x;
-    }
-
-    static char identity(char x) {
-        return x;
-    }
-
-    /**
-     * Identity function on longs.
-     * @param x an arbitrary long value
-     * @return the same value x
-     */
-    static long identity(long x) {
-        return x;
-    }
-
-    static float identity(float x) {
-        return x;
-    }
-
-    static double identity(double x) {
-        return x;
-    }
-
-    private static final MethodHandle IDENTITY, CAST_REFERENCE, IGNORE, EMPTY;
+    private static final MethodHandle CAST_REFERENCE, IGNORE, EMPTY;
     static {
         try {
             MethodType idType = MethodType.genericMethodType(1);
             MethodType ignoreType = idType.changeReturnType(void.class);
-            IDENTITY = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", idType);
             CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType);
             IGNORE = IMPL_LOOKUP.findStatic(THIS_CLASS, "ignore", ignoreType);
             EMPTY = IMPL_LOOKUP.findStatic(THIS_CLASS, "empty", ignoreType.dropParameterTypes(0, 1));
@@ -470,41 +412,6 @@
         return IGNORE;
     }
 
-    public static MethodHandle identity() {
-        return IDENTITY;
-    }
-
-    public static MethodHandle identity(Class<?> type) {
-        if (!type.isPrimitive())
-            // Reference identity has been moved into MethodHandles:
-            return MethodHandles.identity(type);
-        return identity(Wrapper.findPrimitiveType(type));
-    }
-
-    public static MethodHandle identity(Wrapper wrap) {
-        WrapperCache cache = CONSTANT_FUNCTIONS[1];
-        MethodHandle mh = cache.get(wrap);
-        if (mh != null) {
-            return mh;
-        }
-        // slow path
-        MethodType type = MethodType.methodType(wrap.primitiveType());
-        if (wrap != Wrapper.VOID)
-            type = type.appendParameterTypes(wrap.primitiveType());
-        try {
-            mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", type);
-        } catch (ReflectiveOperationException ex) {
-            mh = null;
-        }
-        if (mh == null && wrap == Wrapper.VOID) {
-            mh = EMPTY;  // #(){} : #()void
-        }
-        if (mh != null) {
-            return cache.put(wrap, mh);
-        }
-        throw new IllegalArgumentException("cannot find identity for " + wrap);
-    }
-
     /** Return a method that casts its second argument (an Object) to the given type (a Class). */
     public static MethodHandle cast() {
         return CAST_REFERENCE;
--- a/jdk/test/sun/invoke/util/ValueConversionsTest.java	Wed Sep 10 19:19:50 2014 +0400
+++ b/jdk/test/sun/invoke/util/ValueConversionsTest.java	Wed Sep 10 19:19:50 2014 +0400
@@ -158,14 +158,6 @@
     }
 
     @Test
-    public void testIdentity() throws Throwable {
-        MethodHandle id = ValueConversions.identity();
-        Object expResult = "foo";
-        Object result = id.invokeExact(expResult);
-        assertEquals(expResult, result);
-    }
-
-    @Test
     public void testConvert() throws Throwable {
         for (long tval = 0, ctr = 0;;) {
             if (++ctr > 99999)  throw new AssertionError("too many test values");