8059877: GWT branch frequencies pollution due to LF sharing

Reviewed-by: psandoz, jrose
This commit is contained in:
Vladimir Ivanov 2014-10-29 09:31:37 -07:00
parent 426cd00f3b
commit 224c42ee4d
7 changed files with 210 additions and 41 deletions

View File

@ -44,6 +44,10 @@ abstract class DelegatingMethodHandle extends MethodHandle {
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 @@ abstract class DelegatingMethodHandle extends MethodHandle {
return makeReinvokerForm(target, MethodTypeForm.LF_DELEGATE, DelegatingMethodHandle.class, NF_getTarget);
}
/** 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;
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,
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 @@ abstract class DelegatingMethodHandle extends MethodHandle {
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 @@ abstract class DelegatingMethodHandle extends MethodHandle {
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

View File

@ -628,8 +628,13 @@ class InvokerBytecodeGenerator {
// 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

View File

@ -119,6 +119,7 @@ import static java.lang.invoke.MethodHandleNatives.Constants.*;
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 @@ class LambdaForm {
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 @@ class LambdaForm {
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 @@ class LambdaForm {
}
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 @@ class LambdaForm {
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();

View File

@ -1438,10 +1438,9 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
/*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;

View File

@ -30,6 +30,7 @@ import java.security.PrivilegedAction;
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 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
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 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
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);

View File

@ -47,10 +47,11 @@ import sun.misc.Unsafe;
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 @@ import sun.misc.Unsafe;
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 @@ import sun.misc.Unsafe;
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.

View File

@ -63,24 +63,25 @@ final class MethodTypeForm {
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.