Merge
authorvlivanov
Thu, 12 Feb 2015 13:55:05 -0800
changeset 29024 6d2405beda33
parent 28862 1e61b751d07c (current diff)
parent 29023 550372e828c8 (diff)
child 29032 67b9b02c13fe
Merge
--- a/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java	Thu Feb 05 14:57:53 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java	Thu Feb 12 13:55:05 2015 -0800
@@ -31,7 +31,6 @@
 import sun.invoke.util.VerifyAccess;
 import static java.lang.invoke.MethodHandleNatives.Constants.*;
 import static java.lang.invoke.LambdaForm.*;
-import static java.lang.invoke.LambdaForm.BasicType.*;
 import static java.lang.invoke.MethodTypeForm.*;
 import static java.lang.invoke.MethodHandleStatics.*;
 import java.lang.ref.WeakReference;
@@ -693,4 +692,10 @@
             }
         }
     }
+
+    @Override
+    void customize() {
+        assert(form.customized == null);
+        // No need to customize DMHs.
+    }
 }
--- a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Thu Feb 05 14:57:53 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Thu Feb 12 13:55:05 2015 -0800
@@ -56,9 +56,11 @@
     private static final String OBJ     = "java/lang/Object";
     private static final String OBJARY  = "[Ljava/lang/Object;";
 
+    private static final String MH_SIG  = "L" + MH + ";";
     private static final String LF_SIG  = "L" + LF + ";";
     private static final String LFN_SIG = "L" + LFN + ";";
     private static final String LL_SIG  = "(L" + OBJ + ";)L" + OBJ + ";";
+    private static final String LLV_SIG = "(L" + OBJ + ";L" + OBJ + ";)V";
     private static final String CLL_SIG = "(L" + CLS + ";L" + OBJ + ";)L" + OBJ + ";";
 
     /** Name of its super class*/
@@ -616,6 +618,15 @@
         return g.loadMethod(g.generateCustomizedCodeBytes());
     }
 
+    /** Generates code to check that actual receiver and LambdaForm matches */
+    private boolean checkActualReceiver() {
+        // Expects MethodHandle on the stack and actual receiver MethodHandle in slot #0
+        mv.visitInsn(Opcodes.DUP);
+        mv.visitVarInsn(Opcodes.ALOAD, localsMap[0]);
+        mv.visitMethodInsn(Opcodes.INVOKESTATIC, MHI, "assertSame", LLV_SIG, false);
+        return true;
+    }
+
     /**
      * Generate an invoker method for the passed {@link LambdaForm}.
      */
@@ -635,6 +646,16 @@
             mv.visitAnnotation("Ljava/lang/invoke/DontInline;", true);
         }
 
+        if (lambdaForm.customized != null) {
+            // Since LambdaForm is customized for a particular MethodHandle, it's safe to substitute
+            // receiver MethodHandle (at slot #0) with an embedded constant and use it instead.
+            // It enables more efficient code generation in some situations, since embedded constants
+            // are compile-time constants for JIT compiler.
+            mv.visitLdcInsn(constantPlaceholder(lambdaForm.customized));
+            mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
+            assert(checkActualReceiver()); // expects MethodHandle on top of the stack
+            mv.visitVarInsn(Opcodes.ASTORE, localsMap[0]);
+        }
 
         // iterate over the form's names, generating bytecode instructions for each
         // start iterating at the first name following the arguments
--- a/jdk/src/java.base/share/classes/java/lang/invoke/Invokers.java	Thu Feb 05 14:57:53 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/Invokers.java	Thu Feb 12 13:55:05 2015 -0800
@@ -247,6 +247,7 @@
         int nameCursor = OUTARG_LIMIT;
         final int MTYPE_ARG    = customized ? -1 : nameCursor++;  // might be last in-argument
         final int CHECK_TYPE   = nameCursor++;
+        final int CHECK_CUSTOM = (CUSTOMIZE_THRESHOLD >= 0) ? nameCursor++ : -1;
         final int LINKER_CALL  = nameCursor++;
         MethodType invokerFormType = mtype.invokerType();
         if (isLinker) {
@@ -279,6 +280,9 @@
             // mh.invokeGeneric(a*):R => checkGenericType(mh, TYPEOF(a*:R)).invokeBasic(a*)
             outArgs[0] = names[CHECK_TYPE];
         }
+        if (CHECK_CUSTOM != -1) {
+            names[CHECK_CUSTOM] = new Name(NF_checkCustomized, names[CALL_MH]);
+        }
         names[LINKER_CALL] = new Name(outCallType, outArgs);
         lform = new LambdaForm(debugName, INARG_LIMIT, names);
         if (isLinker)
@@ -386,11 +390,32 @@
         return ((CallSite)site).getTarget();
     }
 
+    /*non-public*/ static
+    @ForceInline
+    void checkCustomized(Object o) {
+        MethodHandle mh = (MethodHandle)o;
+        if (mh.form.customized == null) {
+            maybeCustomize(mh);
+        }
+    }
+
+    /*non-public*/ static
+    @DontInline
+    void maybeCustomize(MethodHandle mh) {
+        byte count = mh.customizationCount;
+        if (count >= CUSTOMIZE_THRESHOLD) {
+            mh.customize();
+        } else {
+            mh.customizationCount = (byte)(count+1);
+        }
+    }
+
     // Local constant functions:
     private static final NamedFunction
         NF_checkExactType,
         NF_checkGenericType,
-        NF_getCallSiteTarget;
+        NF_getCallSiteTarget,
+        NF_checkCustomized;
     static {
         try {
             NamedFunction nfs[] = {
@@ -399,7 +424,9 @@
                 NF_checkGenericType = new NamedFunction(Invokers.class
                         .getDeclaredMethod("checkGenericType", Object.class, Object.class)),
                 NF_getCallSiteTarget = new NamedFunction(Invokers.class
-                        .getDeclaredMethod("getCallSiteTarget", Object.class))
+                        .getDeclaredMethod("getCallSiteTarget", Object.class)),
+                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.
--- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java	Thu Feb 05 14:57:53 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java	Thu Feb 12 13:55:05 2015 -0800
@@ -120,12 +120,14 @@
     final int arity;
     final int result;
     final boolean forceInline;
+    final MethodHandle customized;
     @Stable final Name[] names;
     final String debugName;
     MemberName vmentry;   // low-level behavior, or null if not yet prepared
     private boolean isCompiled;
 
-    volatile Object transformCache;  // managed by LambdaFormEditor
+    // Either a LambdaForm cache (managed by LambdaFormEditor) or a link to uncustomized version (for customized LF)
+    volatile Object transformCache;
 
     public static final int VOID_RESULT = -1, LAST_RESULT = -2;
 
@@ -244,16 +246,17 @@
 
     LambdaForm(String debugName,
                int arity, Name[] names, int result) {
-        this(debugName, arity, names, result, true);
+        this(debugName, arity, names, result, /*forceInline=*/true, /*customized=*/null);
     }
     LambdaForm(String debugName,
-               int arity, Name[] names, int result, boolean forceInline) {
+               int arity, Name[] names, int result, boolean forceInline, MethodHandle customized) {
         assert(namesOK(arity, names));
         this.arity = arity;
         this.result = fixResult(result, names);
         this.names = names.clone();
         this.debugName = fixDebugName(debugName);
         this.forceInline = forceInline;
+        this.customized = customized;
         int maxOutArity = normalize();
         if (maxOutArity > MethodType.MAX_MH_INVOKER_ARITY) {
             // Cannot use LF interpreter on very high arity expressions.
@@ -263,21 +266,21 @@
     }
     LambdaForm(String debugName,
                int arity, Name[] names) {
-        this(debugName, arity, names, LAST_RESULT, true);
+        this(debugName, arity, names, LAST_RESULT, /*forceInline=*/true, /*customized=*/null);
     }
     LambdaForm(String debugName,
                int arity, Name[] names, boolean forceInline) {
-        this(debugName, arity, names, LAST_RESULT, forceInline);
+        this(debugName, arity, names, LAST_RESULT, forceInline, /*customized=*/null);
     }
     LambdaForm(String debugName,
                Name[] formals, Name[] temps, Name result) {
         this(debugName,
-             formals.length, buildNames(formals, temps, result), LAST_RESULT, true);
+             formals.length, buildNames(formals, temps, result), LAST_RESULT, /*forceInline=*/true, /*customized=*/null);
     }
     LambdaForm(String debugName,
                Name[] formals, Name[] temps, Name result, boolean forceInline) {
         this(debugName,
-             formals.length, buildNames(formals, temps, result), LAST_RESULT, forceInline);
+             formals.length, buildNames(formals, temps, result), LAST_RESULT, forceInline, /*customized=*/null);
     }
 
     private static Name[] buildNames(Name[] formals, Name[] temps, Name result) {
@@ -291,10 +294,6 @@
     }
 
     private LambdaForm(String sig) {
-        this(sig, true);
-    }
-
-    private LambdaForm(String sig, boolean forceInline) {
         // Make a blank lambda form, which returns a constant zero or null.
         // It is used as a template for managing the invocation of similar forms that are non-empty.
         // Called only from getPreparedForm.
@@ -303,7 +302,8 @@
         this.result = (signatureReturn(sig) == V_TYPE ? -1 : arity);
         this.names = buildEmptyNames(arity, sig);
         this.debugName = "LF.zero";
-        this.forceInline = forceInline;
+        this.forceInline = true;
+        this.customized = null;
         assert(nameRefsAreLegal());
         assert(isEmpty());
         assert(sig.equals(basicTypeSignature())) : sig + " != " + basicTypeSignature();
@@ -375,6 +375,31 @@
         return true;
     }
 
+    /** Customize LambdaForm for a particular MethodHandle */
+    LambdaForm customize(MethodHandle mh) {
+        LambdaForm customForm = new LambdaForm(debugName, arity, names, result, forceInline, mh);
+        if (COMPILE_THRESHOLD > 0 && isCompiled) {
+            // If shared LambdaForm has been compiled, compile customized version as well.
+            customForm.compileToBytecode();
+        }
+        customForm.transformCache = this; // LambdaFormEditor should always use uncustomized form.
+        return customForm;
+    }
+
+    /** Get uncustomized flavor of the LambdaForm */
+    LambdaForm uncustomize() {
+        if (customized == null) {
+            return this;
+        }
+        assert(transformCache != null); // Customized LambdaForm should always has a link to uncustomized version.
+        LambdaForm uncustomizedForm = (LambdaForm)transformCache;
+        if (COMPILE_THRESHOLD > 0 && isCompiled) {
+            // If customized LambdaForm has been compiled, compile uncustomized version as well.
+            uncustomizedForm.compileToBytecode();
+        }
+        return uncustomizedForm;
+    }
+
     /** Renumber and/or replace params so that they are interned and canonically numbered.
      *  @return maximum argument list length among the names (since we have to pass over them anyway)
      */
@@ -417,8 +442,8 @@
             for (int i = arity; i < names.length; i++) {
                 names[i].internArguments();
             }
-            assert(nameRefsAreLegal());
         }
+        assert(nameRefsAreLegal());
         return maxOutArity;
     }
 
--- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java	Thu Feb 05 14:57:53 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java	Thu Feb 12 13:55:05 2015 -0800
@@ -51,7 +51,10 @@
     static LambdaFormEditor lambdaFormEditor(LambdaForm lambdaForm) {
         // TO DO:  Consider placing intern logic here, to cut down on duplication.
         // lambdaForm = findPreexistingEquivalent(lambdaForm)
-        return new LambdaFormEditor(lambdaForm);
+
+        // Always use uncustomized version for editing.
+        // It helps caching and customized LambdaForms reuse transformCache field to keep a link to uncustomized version.
+        return new LambdaFormEditor(lambdaForm.uncustomize());
     }
 
     /** A description of a cached transform, possibly associated with the result of the transform.
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java	Thu Feb 05 14:57:53 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java	Thu Feb 12 13:55:05 2015 -0800
@@ -434,6 +434,8 @@
     // form is not private so that invokers can easily fetch it
     /*private*/ MethodHandle asTypeCache;
     // asTypeCache is not private so that invokers can easily fetch it
+    /*non-public*/ byte customizationCount;
+    // customizationCount should be accessible from invokers
 
     /**
      * Reports the type of this method handle.
@@ -454,9 +456,9 @@
         type.getClass();  // explicit NPE
         form.getClass();  // explicit NPE
         this.type = type;
-        this.form = form;
+        this.form = form.uncustomize();
 
-        form.prepare();  // TO DO:  Try to delay this step until just before invocation.
+        this.form.prepare();  // TO DO:  Try to delay this step until just before invocation.
     }
 
     /**
@@ -1425,12 +1427,24 @@
      */
     /*non-public*/
     void updateForm(LambdaForm newForm) {
+        assert(newForm.customized == null || newForm.customized == this);
         if (form == newForm)  return;
         newForm.prepare();  // as in MethodHandle.<init>
         UNSAFE.putObject(this, FORM_OFFSET, newForm);
         UNSAFE.fullFence();
     }
 
+    /** Craft a LambdaForm customized for this particular MethodHandle */
+    /*non-public*/
+    void customize() {
+        if (form.customized == null) {
+            LambdaForm newForm = form.customize(this);
+            updateForm(newForm);
+        } else {
+            assert(form.customized == this);
+        }
+    }
+
     private static final long FORM_OFFSET;
     static {
         try {
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Thu Feb 05 14:57:53 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Thu Feb 12 13:55:05 2015 -0800
@@ -597,6 +597,7 @@
         static final NamedFunction NF_checkSpreadArgument;
         static final NamedFunction NF_guardWithCatch;
         static final NamedFunction NF_throwException;
+        static final NamedFunction NF_profileBoolean;
 
         static final MethodHandle MH_castReference;
         static final MethodHandle MH_selectAlternative;
@@ -614,10 +615,12 @@
                 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_castReference        = IMPL_LOOKUP.findStatic(MHI, "castReference",
                                             MethodType.methodType(Object.class, Class.class, Object.class));
@@ -697,7 +700,26 @@
     @LambdaForm.Hidden
     static
     MethodHandle selectAlternative(boolean testResult, MethodHandle target, MethodHandle fallback) {
-        return testResult ? target : fallback;
+        if (testResult) {
+            return target;
+        } else {
+            return fallback;
+        }
+    }
+
+    // Intrinsified by C2. Counters are used during parsing to calculate branch frequencies.
+    @LambdaForm.Hidden
+    static
+    boolean profileBoolean(boolean result, int[] counters) {
+        // Profile is int[2] where [0] and [1] correspond to false and true occurrences respectively.
+        int idx = result ? 1 : 0;
+        try {
+            counters[idx] = Math.addExact(counters[idx], 1);
+        } catch (ArithmeticException e) {
+            // Avoid continuous overflow by halving the problematic count.
+            counters[idx] = counters[idx] / 2;
+        }
+        return result;
     }
 
     static
@@ -708,13 +730,18 @@
         assert(test.type().equals(type.changeReturnType(boolean.class)) && fallback.type().equals(type));
         MethodType basicType = type.basicType();
         LambdaForm form = makeGuardWithTestForm(basicType);
-        BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLL();
         BoundMethodHandle mh;
-
         try {
-            mh = (BoundMethodHandle)
-                    data.constructor().invokeBasic(type, form,
-                        (Object) test, (Object) profile(target), (Object) profile(fallback));
+            if (PROFILE_GWT) {
+                int[] counts = new int[2];
+                mh = (BoundMethodHandle)
+                        BoundMethodHandle.speciesData_LLLL().constructor().invokeBasic(type, form,
+                                (Object) test, (Object) profile(target), (Object) profile(fallback), counts);
+            } else {
+                mh = (BoundMethodHandle)
+                        BoundMethodHandle.speciesData_LLL().constructor().invokeBasic(type, form,
+                                (Object) test, (Object) profile(target), (Object) profile(fallback));
+            }
         } catch (Throwable ex) {
             throw uncaughtException(ex);
         }
@@ -726,7 +753,7 @@
     static
     MethodHandle profile(MethodHandle target) {
         if (DONT_INLINE_THRESHOLD >= 0) {
-            return makeBlockInlningWrapper(target);
+            return makeBlockInliningWrapper(target);
         } else {
             return target;
         }
@@ -737,8 +764,13 @@
      * Corresponding LambdaForm has @DontInline when compiled into bytecode.
      */
     static
-    MethodHandle makeBlockInlningWrapper(MethodHandle target) {
-        LambdaForm lform = PRODUCE_BLOCK_INLINING_FORM.apply(target);
+    MethodHandle makeBlockInliningWrapper(MethodHandle target) {
+        LambdaForm lform;
+        if (DONT_INLINE_THRESHOLD > 0) {
+            lform = PRODUCE_BLOCK_INLINING_FORM.apply(target);
+        } else {
+            lform = PRODUCE_REINVOKER_FORM.apply(target);
+        }
         return new CountingWrapper(target, lform,
                 PRODUCE_BLOCK_INLINING_FORM, PRODUCE_REINVOKER_FORM,
                                    DONT_INLINE_THRESHOLD);
@@ -800,7 +832,7 @@
             MethodHandle wrapper;
             if (isCounting) {
                 LambdaForm lform;
-                lform = countingFormProducer.apply(target);
+                lform = countingFormProducer.apply(newTarget);
                 wrapper = new CountingWrapper(newTarget, lform, countingFormProducer, nonCountingFormProducer, DONT_INLINE_THRESHOLD);
             } else {
                 wrapper = newTarget; // no need for a counting wrapper anymore
@@ -809,7 +841,8 @@
         }
 
         boolean countDown() {
-            if (count <= 0) {
+            int c = count;
+            if (c <= 1) {
                 // Try to limit number of updates. MethodHandle.updateForm() doesn't guarantee LF update visibility.
                 if (isCounting) {
                     isCounting = false;
@@ -818,7 +851,7 @@
                     return false;
                 }
             } else {
-                --count;
+                count = c - 1;
                 return false;
             }
         }
@@ -856,7 +889,10 @@
         final int GET_TEST     = nameCursor++;
         final int GET_TARGET   = nameCursor++;
         final int GET_FALLBACK = nameCursor++;
+        final int GET_COUNTERS = PROFILE_GWT ? nameCursor++ : -1;
         final int CALL_TEST    = nameCursor++;
+        final int PROFILE      = (GET_COUNTERS != -1) ? nameCursor++ : -1;
+        final int TEST         = nameCursor-1; // previous statement: either PROFILE or CALL_TEST
         final int SELECT_ALT   = nameCursor++;
         final int CALL_TARGET  = nameCursor++;
         assert(CALL_TARGET == SELECT_ALT+1);  // must be true to trigger IBG.emitSelectAlternative
@@ -864,12 +900,16 @@
         MethodType lambdaType = basicType.invokerType();
         Name[] names = arguments(nameCursor - ARG_LIMIT, lambdaType);
 
-        BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLL();
+        BoundMethodHandle.SpeciesData data =
+                (GET_COUNTERS != -1) ? BoundMethodHandle.speciesData_LLLL()
+                                     : BoundMethodHandle.speciesData_LLL();
         names[THIS_MH] = names[THIS_MH].withConstraint(data);
         names[GET_TEST]     = new Name(data.getterFunction(0), names[THIS_MH]);
         names[GET_TARGET]   = new Name(data.getterFunction(1), names[THIS_MH]);
         names[GET_FALLBACK] = new Name(data.getterFunction(2), names[THIS_MH]);
-
+        if (GET_COUNTERS != -1) {
+            names[GET_COUNTERS] = new Name(data.getterFunction(3), names[THIS_MH]);
+        }
         Object[] invokeArgs = Arrays.copyOfRange(names, 0, ARG_LIMIT, Object[].class);
 
         // call test
@@ -877,15 +917,18 @@
         invokeArgs[0] = names[GET_TEST];
         names[CALL_TEST] = new Name(testType, invokeArgs);
 
+        // profile branch
+        if (PROFILE != -1) {
+            names[PROFILE] = new Name(Lazy.NF_profileBoolean, names[CALL_TEST], names[GET_COUNTERS]);
+        }
         // call selectAlternative
-        names[SELECT_ALT] = new Name(Lazy.MH_selectAlternative, names[CALL_TEST],
-                                     names[GET_TARGET], names[GET_FALLBACK]);
+        names[SELECT_ALT] = new Name(Lazy.MH_selectAlternative, names[TEST], names[GET_TARGET], names[GET_FALLBACK]);
 
         // call target or fallback
         invokeArgs[0] = names[SELECT_ALT];
         names[CALL_TARGET] = new Name(basicType, invokeArgs);
 
-        lform = new LambdaForm("guard", lambdaType.parameterCount(), names);
+        lform = new LambdaForm("guard", lambdaType.parameterCount(), names, /*forceInline=*/true);
 
         return basicType.form().setCachedLambdaForm(MethodTypeForm.LF_GWT, lform);
     }
@@ -1629,4 +1672,13 @@
         assert(elemType.isPrimitive());
         return Lazy.MH_copyAsPrimitiveArray.bindTo(Wrapper.forPrimitiveType(elemType));
     }
+
+    /*non-public*/ static void assertSame(Object mh1, Object mh2) {
+        if (mh1 != mh2) {
+            String msg = String.format("mh1 != mh2: mh1 = %s (form: %s); mh2 = %s (form: %s)",
+                    mh1, ((MethodHandle)mh1).form,
+                    mh2, ((MethodHandle)mh2).form);
+            throw newInternalError(msg);
+        }
+    }
 }
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java	Thu Feb 05 14:57:53 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java	Thu Feb 12 13:55:05 2015 -0800
@@ -48,9 +48,11 @@
     static final int COMPILE_THRESHOLD;
     static final int DONT_INLINE_THRESHOLD;
     static final int PROFILE_LEVEL;
+    static final boolean PROFILE_GWT;
+    static final int CUSTOMIZE_THRESHOLD;
 
     static {
-        final Object[] values = new Object[7];
+        final Object[] values = new Object[9];
         AccessController.doPrivileged(new PrivilegedAction<Void>() {
                 public Void run() {
                     values[0] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DEBUG_NAMES");
@@ -60,6 +62,8 @@
                     values[4] = Integer.getInteger("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD", 0);
                     values[5] = Integer.getInteger("java.lang.invoke.MethodHandle.DONT_INLINE_THRESHOLD", 30);
                     values[6] = Integer.getInteger("java.lang.invoke.MethodHandle.PROFILE_LEVEL", 0);
+                    values[7] = Boolean.parseBoolean(System.getProperty("java.lang.invoke.MethodHandle.PROFILE_GWT", "true"));
+                    values[8] = Integer.getInteger("java.lang.invoke.MethodHandle.CUSTOMIZE_THRESHOLD", 127);
                     return null;
                 }
             });
@@ -70,6 +74,12 @@
         COMPILE_THRESHOLD         = (Integer) values[4];
         DONT_INLINE_THRESHOLD     = (Integer) values[5];
         PROFILE_LEVEL             = (Integer) values[6];
+        PROFILE_GWT               = (Boolean) values[7];
+        CUSTOMIZE_THRESHOLD       = (Integer) values[8];
+
+        if (CUSTOMIZE_THRESHOLD < -1 || CUSTOMIZE_THRESHOLD > 127) {
+            throw newInternalError("CUSTOMIZE_THRESHOLD should be in [-1...127] range");
+        }
     }
 
     /** Tell if any of the debugging switches are turned on.