8059877: GWT branch frequencies pollution due to LF sharing
Reviewed-by: psandoz, jrose
This commit is contained in:
parent
426cd00f3b
commit
224c42ee4d
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user