8038261: JSR292: cache and reuse typed array accessors
authorvlivanov
Wed, 10 Sep 2014 19:19:47 +0400
changeset 26465 5ff735dd0d52
parent 26464 65b37da18e06
child 26466 3bbb6a284bd4
8038261: JSR292: cache and reuse typed array accessors 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
--- a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Wed Sep 10 19:19:46 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Wed Sep 10 19:19:47 2014 +0400
@@ -35,6 +35,7 @@
 import static java.lang.invoke.LambdaForm.BasicType.*;
 import static java.lang.invoke.MethodHandleStatics.*;
 import static java.lang.invoke.MethodHandleNatives.Constants.*;
+import java.lang.invoke.MethodHandleImpl.ArrayAccessor;
 import sun.invoke.util.ValueConversions;
 import sun.invoke.util.VerifyAccess;
 import sun.invoke.util.VerifyType;
@@ -427,6 +428,40 @@
         emitStoreInsn(L_TYPE, index);
     }
 
+    private byte arrayTypeCode(Wrapper elementType) {
+        switch (elementType) {
+            case BOOLEAN: return Opcodes.T_BOOLEAN;
+            case BYTE:    return Opcodes.T_BYTE;
+            case CHAR:    return Opcodes.T_CHAR;
+            case SHORT:   return Opcodes.T_SHORT;
+            case INT:     return Opcodes.T_INT;
+            case LONG:    return Opcodes.T_LONG;
+            case FLOAT:   return Opcodes.T_FLOAT;
+            case DOUBLE:  return Opcodes.T_DOUBLE;
+            case OBJECT:  return 0; // in place of Opcodes.T_OBJECT
+            default:      throw new InternalError();
+        }
+    }
+
+    private int arrayInsnOpcode(byte tcode, int aaop) throws InternalError {
+        assert(aaop == Opcodes.AASTORE || aaop == Opcodes.AALOAD);
+        int xas;
+        switch (tcode) {
+            case Opcodes.T_BOOLEAN: xas = Opcodes.BASTORE; break;
+            case Opcodes.T_BYTE:    xas = Opcodes.BASTORE; break;
+            case Opcodes.T_CHAR:    xas = Opcodes.CASTORE; break;
+            case Opcodes.T_SHORT:   xas = Opcodes.SASTORE; break;
+            case Opcodes.T_INT:     xas = Opcodes.IASTORE; break;
+            case Opcodes.T_LONG:    xas = Opcodes.LASTORE; break;
+            case Opcodes.T_FLOAT:   xas = Opcodes.FASTORE; break;
+            case Opcodes.T_DOUBLE:  xas = Opcodes.DASTORE; break;
+            case 0:                 xas = Opcodes.AASTORE; break;
+            default:      throw new InternalError();
+        }
+        return xas - Opcodes.AASTORE + aaop;
+    }
+
+
     private void freeFrameLocal(int oldFrameLocal) {
         int i = indexForFrameLocal(oldFrameLocal);
         if (i < 0)  return;
@@ -616,6 +651,10 @@
                 i = i+2; // Jump to the end of GWC idiom
             } else if (isNewArray(rtype, name)) {
                 emitNewArray(rtype, name);
+            } else if (isArrayLoad(member)) {
+                emitArrayLoad(name);
+            } else if (isArrayStore(member)) {
+                emitArrayStore(name);
             } else if (isStaticallyInvocable(member)) {
                 emitStaticInvoke(name);
             } else {
@@ -634,6 +673,35 @@
         return classFile;
     }
 
+    boolean isArrayLoad(MemberName member) {
+        return  member != null &&
+                member.getDeclaringClass() == ArrayAccessor.class &&
+                member.getName() != null &&
+                member.getName().startsWith("getElement");
+    }
+
+    boolean isArrayStore(MemberName member) {
+        return  member != null &&
+                member.getDeclaringClass() == ArrayAccessor.class &&
+                member.getName() != null &&
+                member.getName().startsWith("setElement");
+    }
+
+    void emitArrayLoad(Name name)  { emitArrayOp(name, Opcodes.AALOAD);  }
+    void emitArrayStore(Name name) { emitArrayOp(name, Opcodes.AASTORE); }
+
+    void emitArrayOp(Name name, int arrayOpcode) {
+        assert arrayOpcode == Opcodes.AALOAD || arrayOpcode == Opcodes.AASTORE;
+        Class<?> elementType = name.function.methodType().parameterType(0).getComponentType();
+        assert elementType != null;
+        emitPushArguments(name);
+        if (elementType.isPrimitive()) {
+            Wrapper w = Wrapper.forPrimitiveType(elementType);
+            arrayOpcode = arrayInsnOpcode(arrayTypeCode(w), arrayOpcode);
+        }
+        mv.visitInsn(arrayOpcode);
+    }
+
     /**
      * Emit an invoke for the given name.
      */
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Sep 10 19:19:46 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Sep 10 19:19:47 2014 +0400
@@ -52,27 +52,54 @@
     }
 
     static MethodHandle makeArrayElementAccessor(Class<?> arrayClass, boolean isSetter) {
+        if (arrayClass == Object[].class)
+            return (isSetter ? ArrayAccessor.OBJECT_ARRAY_SETTER : ArrayAccessor.OBJECT_ARRAY_GETTER);
         if (!arrayClass.isArray())
             throw newIllegalArgumentException("not an array: "+arrayClass);
-        MethodHandle accessor = ArrayAccessor.getAccessor(arrayClass, isSetter);
-        MethodType srcType = accessor.type().erase();
-        MethodType lambdaType = srcType.invokerType();
-        Name[] names = arguments(1, lambdaType);
-        Name[] args  = Arrays.copyOfRange(names, 1, 1 + srcType.parameterCount());
-        names[names.length - 1] = new Name(accessor.asType(srcType), (Object[]) args);
-        LambdaForm form = new LambdaForm("getElement", lambdaType.parameterCount(), names);
-        MethodHandle mh = SimpleMethodHandle.make(srcType, form);
-        if (ArrayAccessor.needCast(arrayClass)) {
-            mh = mh.bindTo(arrayClass);
+        MethodHandle[] cache = ArrayAccessor.TYPED_ACCESSORS.get(arrayClass);
+        int cacheIndex = (isSetter ? ArrayAccessor.SETTER_INDEX : ArrayAccessor.GETTER_INDEX);
+        MethodHandle mh = cache[cacheIndex];
+        if (mh != null)  return mh;
+        mh = ArrayAccessor.getAccessor(arrayClass, isSetter);
+        MethodType correctType = ArrayAccessor.correctType(arrayClass, isSetter);
+        if (mh.type() != correctType) {
+            assert(mh.type().parameterType(0) == Object[].class);
+            assert((isSetter ? mh.type().parameterType(2) : mh.type().returnType()) == Object.class);
+            assert(isSetter || correctType.parameterType(0).getComponentType() == correctType.returnType());
+            // safe to view non-strictly, because element type follows from array type
+            mh = mh.viewAsType(correctType);
         }
-        mh = mh.asType(ArrayAccessor.correctType(arrayClass, isSetter));
+        // Atomically update accessor cache.
+        synchronized(cache) {
+            if (cache[cacheIndex] == null) {
+                cache[cacheIndex] = mh;
+            } else {
+                // Throw away newly constructed accessor and use cached version.
+                mh = cache[cacheIndex];
+            }
+        }
         return mh;
     }
 
     static final class ArrayAccessor {
         /// Support for array element access
-        static final HashMap<Class<?>, MethodHandle> GETTER_CACHE = new HashMap<>();  // TODO use it
-        static final HashMap<Class<?>, MethodHandle> SETTER_CACHE = new HashMap<>();  // TODO use it
+        static final int GETTER_INDEX = 0, SETTER_INDEX = 1, INDEX_LIMIT = 2;
+        static final ClassValue<MethodHandle[]> TYPED_ACCESSORS
+                = new ClassValue<MethodHandle[]>() {
+                    @Override
+                    protected MethodHandle[] computeValue(Class<?> type) {
+                        return new MethodHandle[INDEX_LIMIT];
+                    }
+                };
+        static final MethodHandle OBJECT_ARRAY_GETTER, OBJECT_ARRAY_SETTER;
+        static {
+            MethodHandle[] cache = TYPED_ACCESSORS.get(Object[].class);
+            cache[GETTER_INDEX] = OBJECT_ARRAY_GETTER = getAccessor(Object[].class, false);
+            cache[SETTER_INDEX] = OBJECT_ARRAY_SETTER = getAccessor(Object[].class, true);
+
+            assert(InvokerBytecodeGenerator.isStaticallyInvocable(ArrayAccessor.OBJECT_ARRAY_GETTER.internalMemberName()));
+            assert(InvokerBytecodeGenerator.isStaticallyInvocable(ArrayAccessor.OBJECT_ARRAY_SETTER.internalMemberName()));
+        }
 
         static int     getElementI(int[]     a, int i)            { return              a[i]; }
         static long    getElementJ(long[]    a, int i)            { return              a[i]; }
@@ -94,45 +121,21 @@
         static void    setElementC(char[]    a, int i, char    x) {              a[i] = x; }
         static void    setElementL(Object[]  a, int i, Object  x) {              a[i] = x; }
 
-        static Object  getElementL(Class<?> arrayClass, Object[] a, int i)           { arrayClass.cast(a); return a[i]; }
-        static void    setElementL(Class<?> arrayClass, Object[] a, int i, Object x) { arrayClass.cast(a); a[i] = x; }
-
-        // Weakly typed wrappers of Object[] accessors:
-        static Object  getElementL(Object    a, int i)            { return getElementL((Object[])a, i); }
-        static void    setElementL(Object    a, int i, Object  x) {        setElementL((Object[]) a, i, x); }
-        static Object  getElementL(Object   arrayClass, Object a, int i)             { return getElementL((Class<?>) arrayClass, (Object[])a, i); }
-        static void    setElementL(Object   arrayClass, Object a, int i, Object x)   {        setElementL((Class<?>) arrayClass, (Object[])a, i, x); }
-
-        static boolean needCast(Class<?> arrayClass) {
-            Class<?> elemClass = arrayClass.getComponentType();
-            return !elemClass.isPrimitive() && elemClass != Object.class;
-        }
         static String name(Class<?> arrayClass, boolean isSetter) {
             Class<?> elemClass = arrayClass.getComponentType();
             if (elemClass == null)  throw newIllegalArgumentException("not an array", arrayClass);
             return (!isSetter ? "getElement" : "setElement") + Wrapper.basicTypeChar(elemClass);
         }
-        static final boolean USE_WEAKLY_TYPED_ARRAY_ACCESSORS = false;  // FIXME: decide
         static MethodType type(Class<?> arrayClass, boolean isSetter) {
             Class<?> elemClass = arrayClass.getComponentType();
             Class<?> arrayArgClass = arrayClass;
             if (!elemClass.isPrimitive()) {
                 arrayArgClass = Object[].class;
-                if (USE_WEAKLY_TYPED_ARRAY_ACCESSORS)
-                    arrayArgClass = Object.class;
+                elemClass = Object.class;
             }
-            if (!needCast(arrayClass)) {
-                return !isSetter ?
+            return !isSetter ?
                     MethodType.methodType(elemClass,  arrayArgClass, int.class) :
                     MethodType.methodType(void.class, arrayArgClass, int.class, elemClass);
-            } else {
-                Class<?> classArgClass = Class.class;
-                if (USE_WEAKLY_TYPED_ARRAY_ACCESSORS)
-                    classArgClass = Object.class;
-                return !isSetter ?
-                    MethodType.methodType(Object.class, classArgClass, arrayArgClass, int.class) :
-                    MethodType.methodType(void.class,   classArgClass, arrayArgClass, int.class, Object.class);
-            }
         }
         static MethodType correctType(Class<?> arrayClass, boolean isSetter) {
             Class<?> elemClass = arrayClass.getComponentType();