8142334: Improve lazy initialization of java.lang.invoke
authorredestad
Tue, 17 Nov 2015 11:51:45 +0100
changeset 33841 1d1d1ea6a5f2
parent 33840 9e522b05a4b2
child 33842 d261fb6ab872
8142334: Improve lazy initialization of java.lang.invoke Reviewed-by: psandoz, vlivanov, mhaupt
jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java
jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
jdk/src/java.base/share/classes/java/lang/invoke/Invokers.java
jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java
jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java
jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
--- a/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java	Mon Nov 16 22:36:45 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java	Tue Nov 17 11:51:45 2015 +0100
@@ -224,12 +224,12 @@
         assert(names.length == nameCursor);
         if (doesAlloc) {
             // names = { argx,y,z,... new C, init method }
-            names[NEW_OBJ] = new Name(Lazy.NF_allocateInstance, names[DMH_THIS]);
-            names[GET_MEMBER] = new Name(Lazy.NF_constructorMethod, names[DMH_THIS]);
+            names[NEW_OBJ] = new Name(NF_allocateInstance, names[DMH_THIS]);
+            names[GET_MEMBER] = new Name(NF_constructorMethod, names[DMH_THIS]);
         } else if (needsInit) {
-            names[GET_MEMBER] = new Name(Lazy.NF_internalMemberNameEnsureInit, names[DMH_THIS]);
+            names[GET_MEMBER] = new Name(NF_internalMemberNameEnsureInit, names[DMH_THIS]);
         } else {
-            names[GET_MEMBER] = new Name(Lazy.NF_internalMemberName, names[DMH_THIS]);
+            names[GET_MEMBER] = new Name(NF_internalMemberName, names[DMH_THIS]);
         }
         assert(findDirectMethodHandle(names[GET_MEMBER]) == names[DMH_THIS]);
         Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, GET_MEMBER+1, Object[].class);
@@ -250,9 +250,9 @@
     }
 
     static Object findDirectMethodHandle(Name name) {
-        if (name.function == Lazy.NF_internalMemberName ||
-            name.function == Lazy.NF_internalMemberNameEnsureInit ||
-            name.function == Lazy.NF_constructorMethod) {
+        if (name.function == NF_internalMemberName ||
+            name.function == NF_internalMemberNameEnsureInit ||
+            name.function == NF_constructorMethod) {
             assert(name.arguments.length == 1);
             return name.arguments[0];
         }
@@ -613,18 +613,18 @@
         final int RESULT    = nameCursor-1;  // either the call or the cast
         Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
         if (needsInit)
-            names[INIT_BAR] = new Name(Lazy.NF_ensureInitialized, names[DMH_THIS]);
+            names[INIT_BAR] = new Name(NF_ensureInitialized, names[DMH_THIS]);
         if (needsCast && !isGetter)
-            names[PRE_CAST] = new Name(Lazy.NF_checkCast, names[DMH_THIS], names[SET_VALUE]);
+            names[PRE_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[SET_VALUE]);
         Object[] outArgs = new Object[1 + linkerType.parameterCount()];
         assert(outArgs.length == (isGetter ? 3 : 4));
         outArgs[0] = UNSAFE;
         if (isStatic) {
-            outArgs[1] = names[F_HOLDER]  = new Name(Lazy.NF_staticBase, names[DMH_THIS]);
-            outArgs[2] = names[F_OFFSET]  = new Name(Lazy.NF_staticOffset, names[DMH_THIS]);
+            outArgs[1] = names[F_HOLDER]  = new Name(NF_staticBase, names[DMH_THIS]);
+            outArgs[2] = names[F_OFFSET]  = new Name(NF_staticOffset, names[DMH_THIS]);
         } else {
-            outArgs[1] = names[OBJ_CHECK] = new Name(Lazy.NF_checkBase, names[OBJ_BASE]);
-            outArgs[2] = names[F_OFFSET]  = new Name(Lazy.NF_fieldOffset, names[DMH_THIS]);
+            outArgs[1] = names[OBJ_CHECK] = new Name(NF_checkBase, names[OBJ_BASE]);
+            outArgs[2] = names[F_OFFSET]  = new Name(NF_fieldOffset, names[DMH_THIS]);
         }
         if (!isGetter) {
             outArgs[3] = (needsCast ? names[PRE_CAST] : names[SET_VALUE]);
@@ -632,7 +632,7 @@
         for (Object a : outArgs)  assert(a != null);
         names[LINKER_CALL] = new Name(linker, outArgs);
         if (needsCast && isGetter)
-            names[POST_CAST] = new Name(Lazy.NF_checkCast, names[DMH_THIS], names[LINKER_CALL]);
+            names[POST_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[LINKER_CALL]);
         for (Name n : names)  assert(n != null);
         String fieldOrStatic = (isStatic ? "Static" : "Field");
         String lambdaName = (linkerName + fieldOrStatic);  // significant only for debugging
@@ -645,50 +645,45 @@
      * Pre-initialized NamedFunctions for bootstrapping purposes.
      * Factored in an inner class to delay initialization until first usage.
      */
-    private static class Lazy {
-        static final NamedFunction
-                NF_internalMemberName,
-                NF_internalMemberNameEnsureInit,
-                NF_ensureInitialized,
-                NF_fieldOffset,
-                NF_checkBase,
-                NF_staticBase,
-                NF_staticOffset,
-                NF_checkCast,
-                NF_allocateInstance,
-                NF_constructorMethod;
-        static {
-            try {
-                NamedFunction nfs[] = {
-                        NF_internalMemberName = new NamedFunction(DirectMethodHandle.class
-                                .getDeclaredMethod("internalMemberName", Object.class)),
-                        NF_internalMemberNameEnsureInit = new NamedFunction(DirectMethodHandle.class
-                                .getDeclaredMethod("internalMemberNameEnsureInit", Object.class)),
-                        NF_ensureInitialized = new NamedFunction(DirectMethodHandle.class
-                                .getDeclaredMethod("ensureInitialized", Object.class)),
-                        NF_fieldOffset = new NamedFunction(DirectMethodHandle.class
-                                .getDeclaredMethod("fieldOffset", Object.class)),
-                        NF_checkBase = new NamedFunction(DirectMethodHandle.class
-                                .getDeclaredMethod("checkBase", Object.class)),
-                        NF_staticBase = new NamedFunction(DirectMethodHandle.class
-                                .getDeclaredMethod("staticBase", Object.class)),
-                        NF_staticOffset = new NamedFunction(DirectMethodHandle.class
-                                .getDeclaredMethod("staticOffset", Object.class)),
-                        NF_checkCast = new NamedFunction(DirectMethodHandle.class
-                                .getDeclaredMethod("checkCast", Object.class, Object.class)),
-                        NF_allocateInstance = new NamedFunction(DirectMethodHandle.class
-                                .getDeclaredMethod("allocateInstance", Object.class)),
-                        NF_constructorMethod = new NamedFunction(DirectMethodHandle.class
-                                .getDeclaredMethod("constructorMethod", Object.class))
-                };
-                for (NamedFunction nf : nfs) {
-                    // Each nf must be statically invocable or we get tied up in our bootstraps.
-                    assert(InvokerBytecodeGenerator.isStaticallyInvocable(nf.member)) : nf;
-                    nf.resolve();
-                }
-            } catch (ReflectiveOperationException ex) {
-                throw newInternalError(ex);
-            }
+    static final NamedFunction
+            NF_internalMemberName,
+            NF_internalMemberNameEnsureInit,
+            NF_ensureInitialized,
+            NF_fieldOffset,
+            NF_checkBase,
+            NF_staticBase,
+            NF_staticOffset,
+            NF_checkCast,
+            NF_allocateInstance,
+            NF_constructorMethod;
+    static {
+        try {
+            NamedFunction nfs[] = {
+                    NF_internalMemberName = new NamedFunction(DirectMethodHandle.class
+                            .getDeclaredMethod("internalMemberName", Object.class)),
+                    NF_internalMemberNameEnsureInit = new NamedFunction(DirectMethodHandle.class
+                            .getDeclaredMethod("internalMemberNameEnsureInit", Object.class)),
+                    NF_ensureInitialized = new NamedFunction(DirectMethodHandle.class
+                            .getDeclaredMethod("ensureInitialized", Object.class)),
+                    NF_fieldOffset = new NamedFunction(DirectMethodHandle.class
+                            .getDeclaredMethod("fieldOffset", Object.class)),
+                    NF_checkBase = new NamedFunction(DirectMethodHandle.class
+                            .getDeclaredMethod("checkBase", Object.class)),
+                    NF_staticBase = new NamedFunction(DirectMethodHandle.class
+                            .getDeclaredMethod("staticBase", Object.class)),
+                    NF_staticOffset = new NamedFunction(DirectMethodHandle.class
+                            .getDeclaredMethod("staticOffset", Object.class)),
+                    NF_checkCast = new NamedFunction(DirectMethodHandle.class
+                            .getDeclaredMethod("checkCast", Object.class, Object.class)),
+                    NF_allocateInstance = new NamedFunction(DirectMethodHandle.class
+                            .getDeclaredMethod("allocateInstance", Object.class)),
+                    NF_constructorMethod = new NamedFunction(DirectMethodHandle.class
+                            .getDeclaredMethod("constructorMethod", Object.class))
+            };
+            // Each nf must be statically invocable or we get tied up in our bootstraps.
+            assert(InvokerBytecodeGenerator.isStaticallyInvocable(nfs));
+        } catch (ReflectiveOperationException ex) {
+            throw newInternalError(ex);
         }
     }
 }
--- a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Mon Nov 16 22:36:45 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Tue Nov 17 11:51:45 2015 +0100
@@ -750,7 +750,7 @@
         assert(!isLinkerMethodInvoke(name));  // should use the static path for these
         if (true) {
             // push receiver
-            MethodHandle target = name.function.resolvedHandle;
+            MethodHandle target = name.function.resolvedHandle();
             assert(target != null) : name.exprString();
             mv.visitLdcInsn(constantPlaceholder(target));
             emitReferenceCast(MethodHandle.class, target);
@@ -779,6 +779,15 @@
         //MethodHandle.class already covered
     };
 
+    static boolean isStaticallyInvocable(NamedFunction[] functions) {
+        for (NamedFunction nf : functions) {
+            if (!isStaticallyInvocable(nf.member)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     static boolean isStaticallyInvocable(Name name) {
         return isStaticallyInvocable(name.function.member());
     }
@@ -881,7 +890,7 @@
             // The array will be a constant.
             Object emptyArray;
             try {
-                emptyArray = name.function.resolvedHandle.invoke();
+                emptyArray = name.function.resolvedHandle().invoke();
             } catch (Throwable ex) {
                 throw newInternalError(ex);
             }
@@ -1085,8 +1094,8 @@
         Label L_handler = new Label();
         Label L_done = new Label();
 
-        Class<?> returnType = result.function.resolvedHandle.type().returnType();
-        MethodType type = args.function.resolvedHandle.type()
+        Class<?> returnType = result.function.resolvedHandle().type().returnType();
+        MethodType type = args.function.resolvedHandle().type()
                               .dropParameterTypes(0,1)
                               .changeReturnType(returnType);
 
--- a/jdk/src/java.base/share/classes/java/lang/invoke/Invokers.java	Mon Nov 16 22:36:45 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/Invokers.java	Tue Nov 17 11:51:45 2015 +0100
@@ -429,11 +429,8 @@
                 NF_checkCustomized = new NamedFunction(Invokers.class
                         .getDeclaredMethod("checkCustomized", Object.class))
             };
-            for (NamedFunction nf : nfs) {
-                // Each nf must be statically invocable or we get tied up in our bootstraps.
-                assert(InvokerBytecodeGenerator.isStaticallyInvocable(nf.member)) : nf;
-                nf.resolve();
-            }
+            // Each nf must be statically invocable or we get tied up in our bootstraps.
+            assert(InvokerBytecodeGenerator.isStaticallyInvocable(nfs));
         } catch (ReflectiveOperationException ex) {
             throw newInternalError(ex);
         }
--- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java	Mon Nov 16 22:36:45 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java	Tue Nov 17 11:51:45 2015 +0100
@@ -1024,7 +1024,7 @@
 
     static class NamedFunction {
         final MemberName member;
-        @Stable MethodHandle resolvedHandle;
+        private @Stable MethodHandle resolvedHandle;
         @Stable MethodHandle invoker;
 
         NamedFunction(MethodHandle resolvedHandle) {
@@ -1074,8 +1074,10 @@
             return resolvedHandle;
         }
 
-        void resolve() {
-            resolvedHandle = DirectMethodHandle.make(member);
+        synchronized void resolve() {
+            if (resolvedHandle == null) {
+                resolvedHandle = DirectMethodHandle.make(member);
+            }
         }
 
         @Override
@@ -1235,6 +1237,7 @@
                     traceInterpreter("| getInvoker", this);
                     invoker();
                 }
+                // resolvedHandle might be uninitialized, ok for tracing
                 if (resolvedHandle == null) {
                     traceInterpreter("| resolve", this);
                     resolvedHandle();
--- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java	Mon Nov 16 22:36:45 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java	Tue Nov 17 11:51:45 2015 +0100
@@ -541,7 +541,7 @@
         assert(pos > 0);  // cannot spread the MH arg itself
 
         Name spreadParam = new Name(L_TYPE);
-        Name checkSpread = new Name(MethodHandleImpl.Lazy.NF_checkSpreadArgument, spreadParam, arrayLength);
+        Name checkSpread = new Name(MethodHandleImpl.NF_checkSpreadArgument, spreadParam, arrayLength);
 
         // insert the new expressions
         int exprPos = lambdaForm.arity();
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Mon Nov 16 22:36:45 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Tue Nov 17 11:51:45 2015 +0100
@@ -219,7 +219,7 @@
             if (convSpec == null)  continue;
             MethodHandle fn;
             if (convSpec instanceof Class) {
-                fn = Lazy.MH_cast.bindTo(convSpec);
+                fn = getConstantHandle(MH_cast).bindTo(convSpec);
             } else {
                 fn = (MethodHandle) convSpec;
             }
@@ -239,7 +239,7 @@
                 if (convSpec == void.class)
                     fn = null;
                 else
-                    fn = Lazy.MH_cast.bindTo(convSpec);
+                    fn = getConstantHandle(MH_cast).bindTo(convSpec);
             } else {
                 fn = (MethodHandle) convSpec;
             }
@@ -302,7 +302,7 @@
             Name conv;
             if (convSpec instanceof Class) {
                 Class<?> convClass = (Class<?>) convSpec;
-                conv = new Name(Lazy.MH_cast, convClass, names[INARG_BASE + i]);
+                conv = new Name(getConstantHandle(MH_cast), convClass, names[INARG_BASE + i]);
             } else {
                 MethodHandle fn = (MethodHandle) convSpec;
                 conv = new Name(fn, names[INARG_BASE + i]);
@@ -326,7 +326,7 @@
                 conv = new Name(LambdaForm.constantZero(BasicType.basicType(srcType.returnType())));
             } else if (convSpec instanceof Class) {
                 Class<?> convClass = (Class<?>) convSpec;
-                conv = new Name(Lazy.MH_cast, convClass, names[OUT_CALL]);
+                conv = new Name(getConstantHandle(MH_cast), convClass, names[OUT_CALL]);
             } else {
                 MethodHandle fn = (MethodHandle) convSpec;
                 if (fn.type().parameterCount() == 0)
@@ -529,7 +529,7 @@
                 // Spread the array.
                 MethodHandle aload = MethodHandles.arrayElementGetter(spreadArgType);
                 Name array = names[argIndex];
-                names[nameCursor++] = new Name(Lazy.NF_checkSpreadArgument, array, spreadArgCount);
+                names[nameCursor++] = new Name(NF_checkSpreadArgument, array, spreadArgCount);
                 for (int j = 0; j < spreadArgCount; i++, j++) {
                     indexes[i] = nameCursor;
                     names[nameCursor++] = new Name(aload, array, j);
@@ -566,66 +566,6 @@
         throw newIllegalArgumentException("array is not of length "+n);
     }
 
-    /**
-     * Pre-initialized NamedFunctions for bootstrapping purposes.
-     * Factored in an inner class to delay initialization until first usage.
-     */
-    static class Lazy {
-        private static final Class<?> MHI = MethodHandleImpl.class;
-        private static final Class<?> CLS = Class.class;
-
-        private static final MethodHandle[] ARRAYS;
-        private static final MethodHandle[] FILL_ARRAYS;
-
-        static final NamedFunction NF_checkSpreadArgument;
-        static final NamedFunction NF_guardWithCatch;
-        static final NamedFunction NF_throwException;
-        static final NamedFunction NF_profileBoolean;
-
-        static final MethodHandle MH_cast;
-        static final MethodHandle MH_selectAlternative;
-        static final MethodHandle MH_copyAsPrimitiveArray;
-        static final MethodHandle MH_fillNewTypedArray;
-        static final MethodHandle MH_fillNewArray;
-        static final MethodHandle MH_arrayIdentity;
-
-        static {
-            ARRAYS      = makeArrays();
-            FILL_ARRAYS = makeFillArrays();
-
-            try {
-                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_throwException      = new NamedFunction(MHI.getDeclaredMethod("throwException", Throwable.class));
-                NF_profileBoolean      = new NamedFunction(MHI.getDeclaredMethod("profileBoolean", boolean.class, int[].class));
-
-                NF_checkSpreadArgument.resolve();
-                NF_guardWithCatch.resolve();
-                NF_throwException.resolve();
-                NF_profileBoolean.resolve();
-
-                MH_cast                 = IMPL_LOOKUP.findVirtual(CLS, "cast",
-                                            MethodType.methodType(Object.class, Object.class));
-                MH_copyAsPrimitiveArray = IMPL_LOOKUP.findStatic(MHI, "copyAsPrimitiveArray",
-                                            MethodType.methodType(Object.class, Wrapper.class, Object[].class));
-                MH_arrayIdentity        = IMPL_LOOKUP.findStatic(MHI, "identity",
-                                            MethodType.methodType(Object[].class, Object[].class));
-                MH_fillNewArray         = IMPL_LOOKUP.findStatic(MHI, "fillNewArray",
-                                            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);
-            }
-        }
-    }
-
     /** Factory method:  Collect or filter selected argument(s). */
     static MethodHandle makeCollectArguments(MethodHandle target,
                 MethodHandle collector, int collectArgPos, boolean retainOriginalArgs) {
@@ -911,10 +851,10 @@
 
         // profile branch
         if (PROFILE != -1) {
-            names[PROFILE] = new Name(Lazy.NF_profileBoolean, names[CALL_TEST], names[GET_COUNTERS]);
+            names[PROFILE] = new Name(NF_profileBoolean, names[CALL_TEST], names[GET_COUNTERS]);
         }
         // call selectAlternative
-        names[SELECT_ALT] = new Name(Lazy.MH_selectAlternative, names[TEST], names[GET_TARGET], names[GET_FALLBACK]);
+        names[SELECT_ALT] = new Name(getConstantHandle(MH_selectAlternative), names[TEST], names[GET_TARGET], names[GET_FALLBACK]);
 
         // call target or fallback
         invokeArgs[0] = names[SELECT_ALT];
@@ -989,7 +929,7 @@
 
         // 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]};
-        names[TRY_CATCH] = new Name(Lazy.NF_guardWithCatch, gwcArgs);
+        names[TRY_CATCH] = new Name(NF_guardWithCatch, gwcArgs);
 
         // t_{i+2}:I=MethodHandle.invokeBasic(unbox:L,t_{i+1}:L);
         MethodHandle invokeBasicUnbox = MethodHandles.basicInvoker(MethodType.methodType(basicType.rtype(), Object.class));
@@ -1073,7 +1013,7 @@
             mh = MethodHandles.dropArguments(mh, 1, type.parameterList().subList(1, arity));
             return mh;
         }
-        return makePairwiseConvert(Lazy.NF_throwException.resolvedHandle(), type, false, true);
+        return makePairwiseConvert(NF_throwException.resolvedHandle(), type, false, true);
     }
 
     static <T extends Throwable> Empty throwException(T t) throws T { throw t; }
@@ -1421,25 +1361,7 @@
                 { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
 
     private static final int ARRAYS_COUNT = 11;
-
-    private static MethodHandle[] makeArrays() {
-        MethodHandle[] mhs = new MethodHandle[MAX_ARITY + 1];
-        for (int i = 0; i < ARRAYS_COUNT; i++) {
-            MethodHandle mh = findCollector("array", i, Object[].class);
-            mh = makeIntrinsic(mh, Intrinsic.NEW_ARRAY);
-            mhs[i] = mh;
-        }
-        assert(assertArrayMethodCount(mhs));
-        return mhs;
-    }
-
-    private static boolean assertArrayMethodCount(MethodHandle[] mhs) {
-        assert(findCollector("array", ARRAYS_COUNT, Object[].class) == null);
-        for (int i = 0; i < ARRAYS_COUNT; i++) {
-            assert(mhs[i] != null);
-        }
-        return true;
-    }
+    private static final @Stable MethodHandle[] ARRAYS = new MethodHandle[MAX_ARITY + 1];
 
     // filling versions of the above:
     // using Integer len instead of int len and no varargs to avoid bootstrapping problems
@@ -1488,24 +1410,17 @@
                 { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); return a; }
 
     private static final int FILL_ARRAYS_COUNT = 11; // current number of fillArray methods
+    private static final @Stable MethodHandle[] FILL_ARRAYS = new MethodHandle[FILL_ARRAYS_COUNT];
 
-    private static MethodHandle[] makeFillArrays() {
-        MethodHandle[] mhs = new MethodHandle[FILL_ARRAYS_COUNT];
-        mhs[0] = null;  // there is no empty fill; at least a0 is required
-        for (int i = 1; i < FILL_ARRAYS_COUNT; i++) {
-            MethodHandle mh = findCollector("fillArray", i, Object[].class, Integer.class, Object[].class);
-            mhs[i] = mh;
+    private static MethodHandle getFillArray(int count) {
+        assert (count > 0 && count < FILL_ARRAYS_COUNT);
+        MethodHandle mh = FILL_ARRAYS[count];
+        if (mh != null) {
+            return mh;
         }
-        assert(assertFillArrayMethodCount(mhs));
-        return mhs;
-    }
-
-    private static boolean assertFillArrayMethodCount(MethodHandle[] mhs) {
-        assert(findCollector("fillArray", FILL_ARRAYS_COUNT, Object[].class, Integer.class, Object[].class) == null);
-        for (int i = 1; i < FILL_ARRAYS_COUNT; i++) {
-            assert(mhs[i] != null);
-        }
-        return true;
+        mh = findCollector("fillArray", count, Object[].class, Integer.class, Object[].class);
+        FILL_ARRAYS[count] = mh;
+        return mh;
     }
 
     private static Object copyAsPrimitiveArray(Wrapper w, Object... boxes) {
@@ -1518,12 +1433,19 @@
      *  arguments and returns an Object array of them, as if for varargs.
      */
     static MethodHandle varargsArray(int nargs) {
-        MethodHandle mh = Lazy.ARRAYS[nargs];
-        if (mh != null)  return mh;
-        mh = buildVarargsArray(Lazy.MH_fillNewArray, Lazy.MH_arrayIdentity, nargs);
+        MethodHandle mh = ARRAYS[nargs];
+        if (mh != null) {
+            return mh;
+        }
+        if (nargs < ARRAYS_COUNT) {
+            mh = findCollector("array", nargs, Object[].class);
+        } else {
+            mh = buildVarargsArray(getConstantHandle(MH_fillNewArray),
+                    getConstantHandle(MH_arrayIdentity), nargs);
+        }
         assert(assertCorrectArity(mh, nargs));
         mh = makeIntrinsic(mh, Intrinsic.NEW_ARRAY);
-        return Lazy.ARRAYS[nargs] = mh;
+        return ARRAYS[nargs] = mh;
     }
 
     private static boolean assertCorrectArity(MethodHandle mh, int arity) {
@@ -1531,7 +1453,7 @@
         return true;
     }
 
-    // Array identity function (used as Lazy.MH_arrayIdentity).
+    // Array identity function (used as getConstantHandle(MH_arrayIdentity)).
     static <T> T[] identity(T[] x) {
         return x;
     }
@@ -1547,12 +1469,12 @@
         MethodHandle mh = finisher;
         if (rightLen > 0) {
             MethodHandle rightFiller = fillToRight(LEFT_ARGS + rightLen);
-            if (mh == Lazy.MH_arrayIdentity)
+            if (mh.equals(getConstantHandle(MH_arrayIdentity)))
                 mh = rightFiller;
             else
                 mh = MethodHandles.collectArguments(mh, 0, rightFiller);
         }
-        if (mh == Lazy.MH_arrayIdentity)
+        if (mh.equals(getConstantHandle(MH_arrayIdentity)))
             mh = leftCollector;
         else
             mh = MethodHandles.collectArguments(mh, 0, leftCollector);
@@ -1560,7 +1482,7 @@
     }
 
     private static final int LEFT_ARGS = FILL_ARRAYS_COUNT - 1;
-    private static final MethodHandle[] FILL_ARRAY_TO_RIGHT = new MethodHandle[MAX_ARITY+1];
+    private static final @Stable MethodHandle[] FILL_ARRAY_TO_RIGHT = new MethodHandle[MAX_ARITY+1];
     /** fill_array_to_right(N).invoke(a, argL..arg[N-1])
      *  fills a[L]..a[N-1] with corresponding arguments,
      *  and then returns a.  The value L is a global constant (LEFT_ARGS).
@@ -1574,7 +1496,7 @@
     }
     private static MethodHandle buildFiller(int nargs) {
         if (nargs <= LEFT_ARGS)
-            return Lazy.MH_arrayIdentity;  // no args to fill; return the array unchanged
+            return getConstantHandle(MH_arrayIdentity);  // no args to fill; return the array unchanged
         // we need room for both mh and a in mh.invoke(a, arg*[nargs])
         final int CHUNK = LEFT_ARGS;
         int rightLen = nargs % CHUNK;
@@ -1590,7 +1512,7 @@
         if (midLen < LEFT_ARGS) rightLen = nargs - (midLen = LEFT_ARGS);
         assert(rightLen > 0);
         MethodHandle midFill = fillToRight(midLen);  // recursive fill
-        MethodHandle rightFill = Lazy.FILL_ARRAYS[rightLen].bindTo(midLen);  // [midLen..nargs-1]
+        MethodHandle rightFill = getFillArray(rightLen).bindTo(midLen);  // [midLen..nargs-1]
         assert(midFill.type().parameterCount()   == 1 + midLen - LEFT_ARGS);
         assert(rightFill.type().parameterCount() == 1 + rightLen);
 
@@ -1641,14 +1563,14 @@
             Object example = java.lang.reflect.Array.newInstance(arrayType.getComponentType(), 0);
             mh = MethodHandles.constant(arrayType, example);
         } else if (elemType.isPrimitive()) {
-            MethodHandle builder = Lazy.MH_fillNewArray;
+            MethodHandle builder = getConstantHandle(MH_fillNewArray);
             MethodHandle producer = buildArrayProducer(arrayType);
             mh = buildVarargsArray(builder, producer, nargs);
         } else {
             Class<? extends Object[]> objArrayType = arrayType.asSubclass(Object[].class);
             Object[] example = Arrays.copyOf(NO_ARGS_ARRAY, 0, objArrayType);
-            MethodHandle builder = Lazy.MH_fillNewTypedArray.bindTo(example);
-            MethodHandle producer = Lazy.MH_arrayIdentity; // must be weakly typed
+            MethodHandle builder = getConstantHandle(MH_fillNewTypedArray).bindTo(example);
+            MethodHandle producer = getConstantHandle(MH_arrayIdentity); // must be weakly typed
             mh = buildVarargsArray(builder, producer, nargs);
         }
         mh = mh.asType(MethodType.methodType(arrayType, Collections.<Class<?>>nCopies(nargs, elemType)));
@@ -1662,7 +1584,7 @@
     private static MethodHandle buildArrayProducer(Class<?> arrayType) {
         Class<?> elemType = arrayType.getComponentType();
         assert(elemType.isPrimitive());
-        return Lazy.MH_copyAsPrimitiveArray.bindTo(Wrapper.forPrimitiveType(elemType));
+        return getConstantHandle(MH_copyAsPrimitiveArray).bindTo(Wrapper.forPrimitiveType(elemType));
     }
 
     /*non-public*/ static void assertSame(Object mh1, Object mh2) {
@@ -1673,4 +1595,91 @@
             throw newInternalError(msg);
         }
     }
+
+    // Local constant functions:
+    /*non-public*/ static final NamedFunction
+        NF_checkSpreadArgument,
+        NF_guardWithCatch,
+        NF_throwException,
+        NF_profileBoolean;
+
+    static {
+        try {
+            NamedFunction nfs[] = {
+                NF_checkSpreadArgument = new NamedFunction(MethodHandleImpl.class
+                        .getDeclaredMethod("checkSpreadArgument", Object.class, int.class)),
+                NF_guardWithCatch = new NamedFunction(MethodHandleImpl.class
+                        .getDeclaredMethod("guardWithCatch", MethodHandle.class, Class.class,
+                                MethodHandle.class, Object[].class)),
+                NF_throwException = new NamedFunction(MethodHandleImpl.class
+                        .getDeclaredMethod("throwException", Throwable.class)),
+                NF_profileBoolean = new NamedFunction(MethodHandleImpl.class
+                        .getDeclaredMethod("profileBoolean", boolean.class, int[].class))
+            };
+            // Each nf must be statically invocable or we get tied up in our bootstraps.
+            assert(InvokerBytecodeGenerator.isStaticallyInvocable(nfs));
+        } catch (ReflectiveOperationException ex) {
+            throw newInternalError(ex);
+        }
+    }
+
+    // Indexes into constant method handles:
+    private static final int
+            MH_cast                  =  0,
+            MH_selectAlternative     =  1,
+            MH_copyAsPrimitiveArray  =  2,
+            MH_fillNewTypedArray     =  3,
+            MH_fillNewArray          =  4,
+            MH_arrayIdentity         =  5,
+            MH_LIMIT                 =  6;
+
+    private static MethodHandle getConstantHandle(int idx) {
+        MethodHandle handle = HANDLES[idx];
+        if (handle != null) {
+            return handle;
+        }
+        return setCachedHandle(idx, makeConstantHandle(idx));
+    }
+
+    private static synchronized MethodHandle setCachedHandle(int idx, final MethodHandle method) {
+        // Simulate a CAS, to avoid racy duplication of results.
+        MethodHandle prev = HANDLES[idx];
+        if (prev != null) {
+            return prev;
+        }
+        HANDLES[idx] = method;
+        return method;
+    }
+
+    // Local constant method handles:
+    private static final @Stable MethodHandle[] HANDLES = new MethodHandle[MH_LIMIT];
+
+    private static MethodHandle makeConstantHandle(int idx) {
+        try {
+            switch (idx) {
+                case MH_cast:
+                    return IMPL_LOOKUP.findVirtual(Class.class, "cast",
+                            MethodType.methodType(Object.class, Object.class));
+                case MH_copyAsPrimitiveArray:
+                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "copyAsPrimitiveArray",
+                            MethodType.methodType(Object.class, Wrapper.class, Object[].class));
+                case MH_arrayIdentity:
+                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "identity",
+                            MethodType.methodType(Object[].class, Object[].class));
+                case MH_fillNewArray:
+                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "fillNewArray",
+                            MethodType.methodType(Object[].class, Integer.class, Object[].class));
+                case MH_fillNewTypedArray:
+                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "fillNewTypedArray",
+                            MethodType.methodType(Object[].class, Object[].class, Integer.class, Object[].class));
+                case MH_selectAlternative:
+                    return makeIntrinsic(IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "selectAlternative",
+                            MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class)),
+                        Intrinsic.SELECT_ALTERNATIVE);
+            }
+        } catch (ReflectiveOperationException ex) {
+            throw newInternalError(ex);
+        }
+        throw newInternalError("Unknown function index: " + idx);
+    }
 }