8059877: GWT branch frequencies pollution due to LF sharing
authorvlivanov
Wed, 29 Oct 2014 09:31:37 -0700
changeset 27295 8ed31033bf48
parent 27294 dfd89723bd6f
child 27297 f37659460080
8059877: GWT branch frequencies pollution due to LF sharing Reviewed-by: psandoz, jrose
jdk/src/java.base/share/classes/java/lang/invoke/DelegatingMethodHandle.java
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
jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java
jdk/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java
--- a/jdk/src/java.base/share/classes/java/lang/invoke/DelegatingMethodHandle.java	Wed Oct 29 19:59:53 2014 +0300
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/DelegatingMethodHandle.java	Wed Oct 29 09:31:37 2014 -0700
@@ -44,6 +44,10 @@
         super(type, chooseDelegatingForm(target));
     }
 
+    protected DelegatingMethodHandle(MethodType type, LambdaForm form) {
+        super(type, form);
+    }
+
     /** Define this to extract the delegated target which supplies the invocation behavior. */
     abstract protected MethodHandle getTarget();
 
@@ -88,14 +92,31 @@
         return makeReinvokerForm(target, MethodTypeForm.LF_DELEGATE, DelegatingMethodHandle.class, NF_getTarget);
     }
 
+    static LambdaForm makeReinvokerForm(MethodHandle target,
+                                        int whichCache,
+                                        Object constraint,
+                                        NamedFunction getTargetFn) {
+        String debugString;
+        switch(whichCache) {
+            case MethodTypeForm.LF_REBIND:            debugString = "BMH.reinvoke";      break;
+            case MethodTypeForm.LF_DELEGATE:          debugString = "MH.delegate";       break;
+            default:                                  debugString = "MH.reinvoke";       break;
+        }
+        // No pre-action needed.
+        return makeReinvokerForm(target, whichCache, constraint, debugString, true, getTargetFn, null);
+    }
     /** Create a LF which simply reinvokes a target of the given basic type. */
     static LambdaForm makeReinvokerForm(MethodHandle target,
                                         int whichCache,
                                         Object constraint,
-                                        NamedFunction getTargetFn) {
+                                        String debugString,
+                                        boolean forceInline,
+                                        NamedFunction getTargetFn,
+                                        NamedFunction preActionFn) {
         MethodType mtype = target.type().basicType();
         boolean customized = (whichCache < 0 ||
                 mtype.parameterSlotCount() > MethodType.MAX_MH_INVOKER_ARITY);
+        boolean hasPreAction = (preActionFn != null);
         LambdaForm form;
         if (!customized) {
             form = mtype.form().cachedLambdaForm(whichCache);
@@ -105,12 +126,16 @@
         final int ARG_BASE    = 1;
         final int ARG_LIMIT   = ARG_BASE + mtype.parameterCount();
         int nameCursor = ARG_LIMIT;
+        final int PRE_ACTION   = hasPreAction ? nameCursor++ : -1;
         final int NEXT_MH     = customized ? -1 : nameCursor++;
         final int REINVOKE    = nameCursor++;
         LambdaForm.Name[] names = LambdaForm.arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
         assert(names.length == nameCursor);
         names[THIS_DMH] = names[THIS_DMH].withConstraint(constraint);
         Object[] targetArgs;
+        if (hasPreAction) {
+            names[PRE_ACTION] = new LambdaForm.Name(preActionFn, names[THIS_DMH]);
+        }
         if (customized) {
             targetArgs = Arrays.copyOfRange(names, ARG_BASE, ARG_LIMIT, Object[].class);
             names[REINVOKE] = new LambdaForm.Name(target, targetArgs);  // the invoker is the target itself
@@ -120,20 +145,14 @@
             targetArgs[0] = names[NEXT_MH];  // overwrite this MH with next MH
             names[REINVOKE] = new LambdaForm.Name(mtype, targetArgs);
         }
-        String debugString;
-        switch(whichCache) {
-            case MethodTypeForm.LF_REBIND:   debugString = "BMH.reinvoke"; break;
-            case MethodTypeForm.LF_DELEGATE: debugString = "MH.delegate";  break;
-            default:                         debugString = "MH.reinvoke";  break;
-        }
-        form = new LambdaForm(debugString, ARG_LIMIT, names);
+        form = new LambdaForm(debugString, ARG_LIMIT, names, forceInline);
         if (!customized) {
             form = mtype.form().setCachedLambdaForm(whichCache, form);
         }
         return form;
     }
 
-    private static final NamedFunction NF_getTarget;
+    static final NamedFunction NF_getTarget;
     static {
         try {
             NF_getTarget = new NamedFunction(DelegatingMethodHandle.class
--- a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Wed Oct 29 19:59:53 2014 +0300
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Wed Oct 29 09:31:37 2014 -0700
@@ -628,8 +628,13 @@
         // Mark this method as a compiled LambdaForm
         mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Compiled;", true);
 
-        // Force inlining of this invoker method.
-        mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true);
+        if (lambdaForm.forceInline) {
+            // Force inlining of this invoker method.
+            mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true);
+        } else {
+            mv.visitAnnotation("Ljava/lang/invoke/DontInline;", true);
+        }
+
 
         // 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/LambdaForm.java	Wed Oct 29 19:59:53 2014 +0300
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java	Wed Oct 29 09:31:37 2014 -0700
@@ -119,6 +119,7 @@
 class LambdaForm {
     final int arity;
     final int result;
+    final boolean forceInline;
     @Stable final Name[] names;
     final String debugName;
     MemberName vmentry;   // low-level behavior, or null if not yet prepared
@@ -243,11 +244,16 @@
 
     LambdaForm(String debugName,
                int arity, Name[] names, int result) {
+        this(debugName, arity, names, result, true);
+    }
+    LambdaForm(String debugName,
+               int arity, Name[] names, int result, boolean forceInline) {
         assert(namesOK(arity, names));
         this.arity = arity;
         this.result = fixResult(result, names);
         this.names = names.clone();
         this.debugName = fixDebugName(debugName);
+        this.forceInline = forceInline;
         int maxOutArity = normalize();
         if (maxOutArity > MethodType.MAX_MH_INVOKER_ARITY) {
             // Cannot use LF interpreter on very high arity expressions.
@@ -255,17 +261,23 @@
             compileToBytecode();
         }
     }
-
     LambdaForm(String debugName,
                int arity, Name[] names) {
-        this(debugName,
-             arity, names, LAST_RESULT);
+        this(debugName, arity, names, LAST_RESULT, true);
     }
-
+    LambdaForm(String debugName,
+               int arity, Name[] names, boolean forceInline) {
+        this(debugName, arity, names, LAST_RESULT, forceInline);
+    }
     LambdaForm(String debugName,
                Name[] formals, Name[] temps, Name result) {
         this(debugName,
-             formals.length, buildNames(formals, temps, result), LAST_RESULT);
+             formals.length, buildNames(formals, temps, result), LAST_RESULT, true);
+    }
+    LambdaForm(String debugName,
+               Name[] formals, Name[] temps, Name result, boolean forceInline) {
+        this(debugName,
+             formals.length, buildNames(formals, temps, result), LAST_RESULT, forceInline);
     }
 
     private static Name[] buildNames(Name[] formals, Name[] temps, Name result) {
@@ -279,6 +291,10 @@
     }
 
     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.
@@ -287,6 +303,7 @@
         this.result = (signatureReturn(sig) == V_TYPE ? -1 : arity);
         this.names = buildEmptyNames(arity, sig);
         this.debugName = "LF.zero";
+        this.forceInline = forceInline;
         assert(nameRefsAreLegal());
         assert(isEmpty());
         assert(sig.equals(basicTypeSignature())) : sig + " != " + basicTypeSignature();
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java	Wed Oct 29 19:59:53 2014 +0300
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java	Wed Oct 29 09:31:37 2014 -0700
@@ -1438,10 +1438,9 @@
     /*non-public*/
     void updateForm(LambdaForm newForm) {
         if (form == newForm)  return;
-        assert(this instanceof DirectMethodHandle && this.internalMemberName().isStatic());
-        // ISSUE: Should we have a memory fence here?
+        newForm.prepare();  // as in MethodHandle.<init>
         UNSAFE.putObject(this, FORM_OFFSET, newForm);
-        this.form.prepare();  // as in MethodHandle.<init>
+        UNSAFE.fullFence();
     }
 
     private static final long FORM_OFFSET;
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Oct 29 19:59:53 2014 +0300
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Oct 29 09:31:37 2014 -0700
@@ -30,6 +30,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.function.Function;
 
 import sun.invoke.empty.Empty;
 import sun.invoke.util.ValueConversions;
@@ -713,10 +714,11 @@
         LambdaForm form = makeGuardWithTestForm(basicType);
         BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLL();
         BoundMethodHandle mh;
+
         try {
             mh = (BoundMethodHandle)
                     data.constructor().invokeBasic(type, form,
-                        (Object) test, (Object) target, (Object) fallback);
+                        (Object) test, (Object) profile(target), (Object) profile(fallback));
         } catch (Throwable ex) {
             throw uncaughtException(ex);
         }
@@ -724,6 +726,129 @@
         return mh;
     }
 
+
+    static
+    MethodHandle profile(MethodHandle target) {
+        if (DONT_INLINE_THRESHOLD >= 0) {
+            return makeBlockInlningWrapper(target);
+        } else {
+            return target;
+        }
+    }
+
+    /**
+     * Block inlining during JIT-compilation of a target method handle if it hasn't been invoked enough times.
+     * Corresponding LambdaForm has @DontInline when compiled into bytecode.
+     */
+    static
+    MethodHandle makeBlockInlningWrapper(MethodHandle target) {
+        LambdaForm lform = PRODUCE_BLOCK_INLINING_FORM.apply(target);
+        return new CountingWrapper(target, lform,
+                PRODUCE_BLOCK_INLINING_FORM, PRODUCE_REINVOKER_FORM,
+                                   DONT_INLINE_THRESHOLD);
+    }
+
+    /** Constructs reinvoker lambda form which block inlining during JIT-compilation for a particular method handle */
+    private static final Function<MethodHandle, LambdaForm> PRODUCE_BLOCK_INLINING_FORM = new Function<MethodHandle, LambdaForm>() {
+        @Override
+        public LambdaForm apply(MethodHandle target) {
+            return DelegatingMethodHandle.makeReinvokerForm(target,
+                               MethodTypeForm.LF_DELEGATE_BLOCK_INLINING, CountingWrapper.class, "reinvoker.dontInline", false,
+                               DelegatingMethodHandle.NF_getTarget, CountingWrapper.NF_maybeStopCounting);
+        }
+    };
+
+    /** Constructs simple reinvoker lambda form for a particular method handle */
+    private static final Function<MethodHandle, LambdaForm> PRODUCE_REINVOKER_FORM = new Function<MethodHandle, LambdaForm>() {
+        @Override
+        public LambdaForm apply(MethodHandle target) {
+            return DelegatingMethodHandle.makeReinvokerForm(target,
+                    MethodTypeForm.LF_DELEGATE, DelegatingMethodHandle.class, DelegatingMethodHandle.NF_getTarget);
+        }
+    };
+
+    /**
+     * Counting method handle. It has 2 states: counting and non-counting.
+     * It is in counting state for the first n invocations and then transitions to non-counting state.
+     * Behavior in counting and non-counting states is determined by lambda forms produced by
+     * countingFormProducer & nonCountingFormProducer respectively.
+     */
+    static class CountingWrapper extends DelegatingMethodHandle {
+        private final MethodHandle target;
+        private int count;
+        private Function<MethodHandle, LambdaForm> countingFormProducer;
+        private Function<MethodHandle, LambdaForm> nonCountingFormProducer;
+        private volatile boolean isCounting;
+
+        private CountingWrapper(MethodHandle target, LambdaForm lform,
+                                Function<MethodHandle, LambdaForm> countingFromProducer,
+                                Function<MethodHandle, LambdaForm> nonCountingFormProducer,
+                                int count) {
+            super(target.type(), lform);
+            this.target = target;
+            this.count = count;
+            this.countingFormProducer = countingFromProducer;
+            this.nonCountingFormProducer = nonCountingFormProducer;
+            this.isCounting = (count > 0);
+        }
+
+        @Hidden
+        @Override
+        protected MethodHandle getTarget() {
+            return target;
+        }
+
+        @Override
+        public MethodHandle asTypeUncached(MethodType newType) {
+            MethodHandle newTarget = target.asType(newType);
+            MethodHandle wrapper;
+            if (isCounting) {
+                LambdaForm lform;
+                lform = countingFormProducer.apply(target);
+                wrapper = new CountingWrapper(newTarget, lform, countingFormProducer, nonCountingFormProducer, DONT_INLINE_THRESHOLD);
+            } else {
+                wrapper = newTarget; // no need for a counting wrapper anymore
+            }
+            return (asTypeCache = wrapper);
+        }
+
+        boolean countDown() {
+            if (count <= 0) {
+                // Try to limit number of updates. MethodHandle.updateForm() doesn't guarantee LF update visibility.
+                if (isCounting) {
+                    isCounting = false;
+                    return true;
+                } else {
+                    return false;
+                }
+            } else {
+                --count;
+                return false;
+            }
+        }
+
+        @Hidden
+        static void maybeStopCounting(Object o1) {
+             CountingWrapper wrapper = (CountingWrapper) o1;
+             if (wrapper.countDown()) {
+                 // Reached invocation threshold. Replace counting behavior with a non-counting one.
+                 LambdaForm lform = wrapper.nonCountingFormProducer.apply(wrapper.target);
+                 lform.compileToBytecode(); // speed up warmup by avoiding LF interpretation again after transition
+                 wrapper.updateForm(lform);
+             }
+        }
+
+        static final NamedFunction NF_maybeStopCounting;
+        static {
+            Class<?> THIS_CLASS = CountingWrapper.class;
+            try {
+                NF_maybeStopCounting = new NamedFunction(THIS_CLASS.getDeclaredMethod("maybeStopCounting", Object.class));
+            } catch (ReflectiveOperationException ex) {
+                throw newInternalError(ex);
+            }
+        }
+    }
+
     static
     LambdaForm makeGuardWithTestForm(MethodType basicType) {
         LambdaForm lform = basicType.form().cachedLambdaForm(MethodTypeForm.LF_GWT);
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java	Wed Oct 29 19:59:53 2014 +0300
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java	Wed Oct 29 09:31:37 2014 -0700
@@ -47,10 +47,11 @@
     static final boolean TRACE_METHOD_LINKAGE;
     static final boolean USE_LAMBDA_FORM_EDITOR;
     static final int COMPILE_THRESHOLD;
+    static final int DONT_INLINE_THRESHOLD;
     static final int PROFILE_LEVEL;
 
     static {
-        final Object[] values = { false, false, false, false, false, null, null };
+        final Object[] values = new Object[8];
         AccessController.doPrivileged(new PrivilegedAction<Void>() {
                 public Void run() {
                     values[0] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DEBUG_NAMES");
@@ -59,7 +60,8 @@
                     values[3] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE");
                     values[4] = Boolean.getBoolean("java.lang.invoke.MethodHandle.USE_LF_EDITOR");
                     values[5] = Integer.getInteger("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD", 30);
-                    values[6] = Integer.getInteger("java.lang.invoke.MethodHandle.PROFILE_LEVEL", 0);
+                    values[6] = Integer.getInteger("java.lang.invoke.MethodHandle.DONT_INLINE_THRESHOLD", 30);
+                    values[7] = Integer.getInteger("java.lang.invoke.MethodHandle.PROFILE_LEVEL", 0);
                     return null;
                 }
             });
@@ -69,7 +71,8 @@
         TRACE_METHOD_LINKAGE      = (Boolean) values[3];
         USE_LAMBDA_FORM_EDITOR    = (Boolean) values[4];
         COMPILE_THRESHOLD         = (Integer) values[5];
-        PROFILE_LEVEL             = (Integer) values[6];
+        DONT_INLINE_THRESHOLD     = (Integer) values[6];
+        PROFILE_LEVEL             = (Integer) values[7];
     }
 
     /** Tell if any of the debugging switches are turned on.
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java	Wed Oct 29 19:59:53 2014 +0300
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java	Wed Oct 29 09:31:37 2014 -0700
@@ -63,24 +63,25 @@
     final @Stable LambdaForm[] lambdaForms;
     // Indexes into lambdaForms:
     static final int
-            LF_INVVIRTUAL     =  0,  // DMH invokeVirtual
-            LF_INVSTATIC      =  1,
-            LF_INVSPECIAL     =  2,
-            LF_NEWINVSPECIAL  =  3,
-            LF_INVINTERFACE   =  4,
-            LF_INVSTATIC_INIT =  5,  // DMH invokeStatic with <clinit> barrier
-            LF_INTERPRET      =  6,  // LF interpreter
-            LF_REBIND         =  7,  // BoundMethodHandle
-            LF_DELEGATE       =  8,  // DelegatingMethodHandle
-            LF_EX_LINKER      =  9,  // invokeExact_MT (for invokehandle)
-            LF_EX_INVOKER     = 10,  // MHs.invokeExact
-            LF_GEN_LINKER     = 11,  // generic invoke_MT (for invokehandle)
-            LF_GEN_INVOKER    = 12,  // generic MHs.invoke
-            LF_CS_LINKER      = 13,  // linkToCallSite_CS
-            LF_MH_LINKER      = 14,  // linkToCallSite_MH
-            LF_GWC            = 15,  // guardWithCatch (catchException)
-            LF_GWT            = 16,  // guardWithTest
-            LF_LIMIT          = 17;
+            LF_INVVIRTUAL              =  0,  // DMH invokeVirtual
+            LF_INVSTATIC               =  1,
+            LF_INVSPECIAL              =  2,
+            LF_NEWINVSPECIAL           =  3,
+            LF_INVINTERFACE            =  4,
+            LF_INVSTATIC_INIT          =  5,  // DMH invokeStatic with <clinit> barrier
+            LF_INTERPRET               =  6,  // LF interpreter
+            LF_REBIND                  =  7,  // BoundMethodHandle
+            LF_DELEGATE                =  8,  // DelegatingMethodHandle
+            LF_DELEGATE_BLOCK_INLINING =  9,  // Counting DelegatingMethodHandle w/ @DontInline
+            LF_EX_LINKER               = 10,  // invokeExact_MT (for invokehandle)
+            LF_EX_INVOKER              = 11,  // MHs.invokeExact
+            LF_GEN_LINKER              = 12,  // generic invoke_MT (for invokehandle)
+            LF_GEN_INVOKER             = 13,  // generic MHs.invoke
+            LF_CS_LINKER               = 14,  // linkToCallSite_CS
+            LF_MH_LINKER               = 15,  // linkToCallSite_MH
+            LF_GWC                     = 16,  // guardWithCatch (catchException)
+            LF_GWT                     = 17,  // guardWithTest
+            LF_LIMIT                   = 18;
 
     /** Return the type corresponding uniquely (1-1) to this MT-form.
      *  It might have any primitive returns or arguments, but will have no references except Object.