8050200: Make LambdaForm intrinsics detection more robust
authorvlivanov
Wed, 10 Sep 2014 19:19:50 +0400
changeset 26473 0abc9399a1a6
parent 26472 71b6a6f208c0
child 26474 655d08549e43
8050200: Make LambdaForm intrinsics detection more robust 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/LambdaForm.java
jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.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:49 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Wed Sep 10 19:19:50 2014 +0400
@@ -35,7 +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.VerifyAccess;
 import sun.invoke.util.VerifyType;
 import sun.invoke.util.Wrapper;
@@ -636,26 +636,44 @@
         Name onStack = null;
         for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) {
             Name name = lambdaForm.names[i];
-            MemberName member = name.function.member();
-            Class<?> rtype = name.function.methodType().returnType();
 
             emitStoreResult(onStack);
             onStack = name;  // unless otherwise modified below
+            MethodHandleImpl.Intrinsic intr = name.function.intrinsicName();
+            switch (intr) {
+                case SELECT_ALTERNATIVE:
+                    assert isSelectAlternative(i);
+                    onStack = emitSelectAlternative(name, lambdaForm.names[i+1]);
+                    i++;  // skip MH.invokeBasic of the selectAlternative result
+                    continue;
+                case GUARD_WITH_CATCH:
+                    assert isGuardWithCatch(i);
+                    onStack = emitGuardWithCatch(i);
+                    i = i+2; // Jump to the end of GWC idiom
+                    continue;
+                case NEW_ARRAY:
+                    Class<?> rtype = name.function.methodType().returnType();
+                    if (isStaticallyNameable(rtype)) {
+                        emitNewArray(name);
+                        continue;
+                    }
+                    break;
+                case ARRAY_LOAD:
+                    emitArrayLoad(name);
+                    continue;
+                case ARRAY_STORE:
+                    emitArrayStore(name);
+                    continue;
+                case NONE:
+                    // no intrinsic associated
+                    break;
+                default:
+                    throw newInternalError("Unknown intrinsic: "+intr);
+            }
 
-            if (isSelectAlternative(i)) {
-                onStack = emitSelectAlternative(name, lambdaForm.names[i + 1]);
-                i++;  // skip MH.invokeBasic of the selectAlternative result
-            } else if (isGuardWithCatch(i)) {
-                onStack = emitGuardWithCatch(i);
-                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);
+            MemberName member = name.function.member();
+            if (isStaticallyInvocable(member)) {
+                emitStaticInvoke(member, name);
             } else {
                 emitInvoke(name);
             }
@@ -672,20 +690,6 @@
         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); }
 
@@ -837,33 +841,31 @@
         }
     }
 
-    boolean isNewArray(Class<?> rtype, Name name) {
-        return rtype.isArray() &&
-                isStaticallyNameable(rtype) &&
-                isArrayBuilder(name.function.resolvedHandle) &&
-                name.arguments.length > 0;
-    }
-
-    void emitNewArray(Class<?> rtype, Name name) throws InternalError {
+    void emitNewArray(Name name) throws InternalError {
+        Class<?> rtype = name.function.methodType().returnType();
+        if (name.arguments.length == 0) {
+            // The array will be a constant.
+            Object emptyArray;
+            try {
+                emptyArray = name.function.resolvedHandle.invoke();
+            } catch (Throwable ex) {
+                throw newInternalError(ex);
+            }
+            assert(java.lang.reflect.Array.getLength(emptyArray) == 0);
+            assert(emptyArray.getClass() == rtype);  // exact typing
+            mv.visitLdcInsn(constantPlaceholder(emptyArray));
+            emitReferenceCast(rtype, emptyArray);
+            return;
+        }
         Class<?> arrayElementType = rtype.getComponentType();
+        assert(arrayElementType != null);
         emitIconstInsn(name.arguments.length);
-        int xas;
+        int xas = Opcodes.AASTORE;
         if (!arrayElementType.isPrimitive()) {
             mv.visitTypeInsn(Opcodes.ANEWARRAY, getInternalName(arrayElementType));
-            xas = Opcodes.AASTORE;
         } else {
-            int tc;
-            switch (Wrapper.forPrimitiveType(arrayElementType)) {
-            case BOOLEAN: tc = Opcodes.T_BOOLEAN; xas = Opcodes.BASTORE; break;
-            case BYTE:    tc = Opcodes.T_BYTE;    xas = Opcodes.BASTORE; break;
-            case CHAR:    tc = Opcodes.T_CHAR;    xas = Opcodes.CASTORE; break;
-            case SHORT:   tc = Opcodes.T_SHORT;   xas = Opcodes.SASTORE; break;
-            case INT:     tc = Opcodes.T_INT;     xas = Opcodes.IASTORE; break;
-            case LONG:    tc = Opcodes.T_LONG;    xas = Opcodes.LASTORE; break;
-            case FLOAT:   tc = Opcodes.T_FLOAT;   xas = Opcodes.FASTORE; break;
-            case DOUBLE:  tc = Opcodes.T_DOUBLE;  xas = Opcodes.DASTORE; break;
-            default:      throw new InternalError(rtype.getName());
-            }
+            byte tc = arrayTypeCode(Wrapper.forPrimitiveType(arrayElementType));
+            xas = arrayInsnOpcode(tc, xas);
             mv.visitIntInsn(Opcodes.NEWARRAY, tc);
         }
         // store arguments
@@ -890,24 +892,6 @@
         throw new InternalError("refKind="+refKind);
     }
 
-    static boolean isArrayBuilder(MethodHandle fn) {
-        if (fn == null)
-            return false;
-        MethodType mtype = fn.type();
-        Class<?> rtype = mtype.returnType();
-        Class<?> arrayElementType = rtype.getComponentType();
-        if (arrayElementType == null)
-            return false;
-        List<Class<?>> ptypes = mtype.parameterList();
-        int size = ptypes.size();
-        if (!ptypes.equals(Collections.nCopies(size, arrayElementType)))
-            return false;
-        // Assume varargsArray caches pointers.
-        if (fn != MethodHandleImpl.varargsArray(rtype, size))
-            return false;
-        return true;
-    }
-
     /**
      * Check if MemberName is a call to a method named {@code name} in class {@code declaredClass}.
      */
--- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java	Wed Sep 10 19:19:49 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java	Wed Sep 10 19:19:50 2014 +0400
@@ -27,11 +27,10 @@
 
 import java.lang.annotation.*;
 import java.lang.reflect.Method;
-import java.util.Map;
 import java.util.List;
 import java.util.Arrays;
 import java.util.HashMap;
-import java.util.concurrent.ConcurrentHashMap;
+
 import sun.invoke.util.Wrapper;
 import java.lang.reflect.Field;
 
@@ -1371,6 +1370,11 @@
         public boolean isConstantZero() {
             return this.equals(constantZero(returnType()));
         }
+
+        public MethodHandleImpl.Intrinsic intrinsicName() {
+            return resolvedHandle == null ? MethodHandleImpl.Intrinsic.NONE
+                                          : resolvedHandle.intrinsicName();
+        }
     }
 
     public static String basicTypeSignature(MethodType type) {
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java	Wed Sep 10 19:19:49 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java	Wed Sep 10 19:19:50 2014 +0400
@@ -27,11 +27,8 @@
 
 
 import java.util.*;
-import java.lang.invoke.LambdaForm.BasicType;
-import sun.misc.Unsafe;
 
 import static java.lang.invoke.MethodHandleStatics.*;
-import static java.lang.invoke.LambdaForm.BasicType.*;
 
 /**
  * A method handle is a typed, directly executable reference to an underlying method,
@@ -1356,6 +1353,12 @@
     }
 
     /*non-public*/
+    MethodHandleImpl.Intrinsic intrinsicName() {
+        // no special intrinsic meaning to most MHs
+        return MethodHandleImpl.Intrinsic.NONE;
+    }
+
+    /*non-public*/
     MethodHandle withInternalMemberName(MemberName member, boolean isInvokeSpecial) {
         if (member != null) {
             return MethodHandleImpl.makeWrappedMember(this, member, isInvokeSpecial);
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Sep 10 19:19:49 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Sep 10 19:19:50 2014 +0400
@@ -30,7 +30,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.List;
 
 import sun.invoke.empty.Empty;
 import sun.invoke.util.ValueConversions;
@@ -86,6 +85,7 @@
             // safe to view non-strictly, because element type follows from array type
             mh = mh.viewAsType(correctType, false);
         }
+        mh = makeIntrinsic(mh, (isSetter ? Intrinsic.ARRAY_STORE : Intrinsic.ARRAY_LOAD));
         // Atomically update accessor cache.
         synchronized(cache) {
             if (cache[cacheIndex] == null) {
@@ -111,8 +111,8 @@
         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);
+            cache[GETTER_INDEX] = OBJECT_ARRAY_GETTER = makeIntrinsic(getAccessor(Object[].class, false), Intrinsic.ARRAY_LOAD);
+            cache[SETTER_INDEX] = OBJECT_ARRAY_SETTER = makeIntrinsic(getAccessor(Object[].class, true),  Intrinsic.ARRAY_STORE);
 
             assert(InvokerBytecodeGenerator.isStaticallyInvocable(ArrayAccessor.OBJECT_ARRAY_GETTER.internalMemberName()));
             assert(InvokerBytecodeGenerator.isStaticallyInvocable(ArrayAccessor.OBJECT_ARRAY_SETTER.internalMemberName()));
@@ -502,10 +502,10 @@
 
         static final NamedFunction NF_checkSpreadArgument;
         static final NamedFunction NF_guardWithCatch;
-        static final NamedFunction NF_selectAlternative;
         static final NamedFunction NF_throwException;
 
         static final MethodHandle MH_castReference;
+        static final MethodHandle MH_selectAlternative;
         static final MethodHandle MH_copyAsPrimitiveArray;
         static final MethodHandle MH_fillNewTypedArray;
         static final MethodHandle MH_fillNewArray;
@@ -516,13 +516,10 @@
                 NF_checkSpreadArgument = new NamedFunction(MHI.getDeclaredMethod("checkSpreadArgument", Object.class, int.class));
                 NF_guardWithCatch      = new NamedFunction(MHI.getDeclaredMethod("guardWithCatch", MethodHandle.class, Class.class,
                                                                                  MethodHandle.class, Object[].class));
-                NF_selectAlternative   = new NamedFunction(MHI.getDeclaredMethod("selectAlternative", boolean.class, MethodHandle.class,
-                                                                                 MethodHandle.class));
                 NF_throwException      = new NamedFunction(MHI.getDeclaredMethod("throwException", Throwable.class));
 
                 NF_checkSpreadArgument.resolve();
                 NF_guardWithCatch.resolve();
-                NF_selectAlternative.resolve();
                 NF_throwException.resolve();
 
                 MH_castReference        = IMPL_LOOKUP.findStatic(MHI, "castReference",
@@ -535,6 +532,11 @@
                                             MethodType.methodType(Object[].class, Integer.class, Object[].class));
                 MH_fillNewTypedArray    = IMPL_LOOKUP.findStatic(MHI, "fillNewTypedArray",
                                             MethodType.methodType(Object[].class, Object[].class, Integer.class, Object[].class));
+
+                MH_selectAlternative    = makeIntrinsic(
+                        IMPL_LOOKUP.findStatic(MHI, "selectAlternative",
+                                MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class)),
+                        Intrinsic.SELECT_ALTERNATIVE);
             } catch (ReflectiveOperationException ex) {
                 throw newInternalError(ex);
             }
@@ -620,7 +622,7 @@
 
         // call selectAlternative
         Object[] selectArgs = { names[arity + 1], target, fallback };
-        names[arity + 2] = new Name(Lazy.NF_selectAlternative, selectArgs);
+        names[arity + 2] = new Name(Lazy.MH_selectAlternative, selectArgs);
         targetArgs[0] = names[arity + 2];
 
         // call target or fallback
@@ -689,7 +691,7 @@
         Object[] args = new Object[invokeBasic.type().parameterCount()];
         args[0] = names[GET_COLLECT_ARGS];
         System.arraycopy(names, ARG_BASE, args, 1, ARG_LIMIT-ARG_BASE);
-        names[BOXED_ARGS] = new Name(new NamedFunction(invokeBasic), args);
+        names[BOXED_ARGS] = new Name(makeIntrinsic(invokeBasic, Intrinsic.GUARD_WITH_CATCH), args);
 
         // t_{i+1}:L=MethodHandleImpl.guardWithCatch(target:L,exType:L,catcher:L,t_{i}:L);
         Object[] gwcArgs = new Object[] {names[GET_TARGET], names[GET_CLASS], names[GET_CATCHER], names[BOXED_ARGS]};
@@ -698,7 +700,7 @@
         // t_{i+2}:I=MethodHandle.invokeBasic(unbox:L,t_{i+1}:L);
         MethodHandle invokeBasicUnbox = MethodHandles.basicInvoker(MethodType.methodType(basicType.rtype(), Object.class));
         Object[] unboxArgs  = new Object[] {names[GET_UNBOX_RESULT], names[TRY_CATCH]};
-        names[UNBOX_RESULT] = new Name(new NamedFunction(invokeBasicUnbox), unboxArgs);
+        names[UNBOX_RESULT] = new Name(invokeBasicUnbox, unboxArgs);
 
         lform = new LambdaForm("guardWithCatch", lambdaType.parameterCount(), names);
 
@@ -1004,6 +1006,63 @@
         return new WrappedMember(target, target.type(), member, isInvokeSpecial, null);
     }
 
+    /** Intrinsic IDs */
+    /*non-public*/
+    enum Intrinsic {
+        SELECT_ALTERNATIVE,
+        GUARD_WITH_CATCH,
+        NEW_ARRAY,
+        ARRAY_LOAD,
+        ARRAY_STORE,
+        NONE // no intrinsic associated
+    }
+
+    /** Mark arbitrary method handle as intrinsic.
+     * InvokerBytecodeGenerator uses this info to produce more efficient bytecode shape. */
+    private static final class IntrinsicMethodHandle extends DelegatingMethodHandle {
+        private final MethodHandle target;
+        private final Intrinsic intrinsicName;
+
+        IntrinsicMethodHandle(MethodHandle target, Intrinsic intrinsicName) {
+            super(target.type(), target);
+            this.target = target;
+            this.intrinsicName = intrinsicName;
+        }
+
+        @Override
+        protected MethodHandle getTarget() {
+            return target;
+        }
+
+        @Override
+        Intrinsic intrinsicName() {
+            return intrinsicName;
+        }
+
+        @Override
+        public MethodHandle asTypeUncached(MethodType newType) {
+            // This MH is an alias for target, except for the intrinsic name
+            // Drop the name if there is any conversion.
+            return asTypeCache = target.asType(newType);
+        }
+
+        @Override
+        String internalProperties() {
+            return super.internalProperties() +
+                    "\n& Intrinsic="+intrinsicName;
+        }
+    }
+
+    static MethodHandle makeIntrinsic(MethodHandle target, Intrinsic intrinsicName) {
+        if (intrinsicName == target.intrinsicName())
+            return target;
+        return new IntrinsicMethodHandle(target, intrinsicName);
+    }
+
+    static MethodHandle makeIntrinsic(MethodType type, LambdaForm form, Intrinsic intrinsicName) {
+        return new IntrinsicMethodHandle(SimpleMethodHandle.make(type, form), intrinsicName);
+    }
+
     /// Collection of multiple arguments.
 
     private static MethodHandle findCollector(String name, int nargs, Class<?> rtype, Class<?>... ptypes) {
@@ -1053,6 +1112,7 @@
         for (;;) {
             MethodHandle mh = findCollector("array", mhs.size(), Object[].class);
             if (mh == null)  break;
+            mh = makeIntrinsic(mh, Intrinsic.NEW_ARRAY);
             mhs.add(mh);
         }
         assert(mhs.size() == 11);  // current number of methods
@@ -1131,9 +1191,11 @@
         MethodHandle mh = ARRAYS[nargs];
         if (mh != null)  return mh;
         mh = findCollector("array", nargs, Object[].class);
+        if (mh != null)  mh = makeIntrinsic(mh, Intrinsic.NEW_ARRAY);
         if (mh != null)  return ARRAYS[nargs] = mh;
         mh = buildVarargsArray(Lazy.MH_fillNewArray, Lazy.MH_arrayIdentity, nargs);
         assert(assertCorrectArity(mh, nargs));
+        mh = makeIntrinsic(mh, Intrinsic.NEW_ARRAY);
         return ARRAYS[nargs] = mh;
     }
 
@@ -1263,6 +1325,7 @@
             mh = buildVarargsArray(builder, producer, nargs);
         }
         mh = mh.asType(MethodType.methodType(arrayType, Collections.<Class<?>>nCopies(nargs, elemType)));
+        mh = makeIntrinsic(mh, Intrinsic.NEW_ARRAY);
         assert(assertCorrectArity(mh, nargs));
         if (nargs < cache.length)
             cache[nargs] = mh;