8059877: GWT branch frequencies pollution due to LF sharing
Reviewed-by: psandoz, jrose
--- 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.