Merge pull request #18323 from uschindler/dynamiccallsite_cleanup
painless: cleanup of DynamicCallSite
This commit is contained in:
commit
520697eb14
|
@ -39,10 +39,12 @@ import java.lang.invoke.MutableCallSite;
|
||||||
* Based on the cascaded inlining cache from the JSR 292 cookbook
|
* Based on the cascaded inlining cache from the JSR 292 cookbook
|
||||||
* (https://code.google.com/archive/p/jsr292-cookbook/, BSD license)
|
* (https://code.google.com/archive/p/jsr292-cookbook/, BSD license)
|
||||||
*/
|
*/
|
||||||
// NOTE: this class must be public, because generated painless classes are in a different package,
|
// NOTE: this class must be public, because generated painless classes are in a different classloader,
|
||||||
// and it needs to be accessible by that code.
|
// and it needs to be accessible by that code.
|
||||||
public final class DynamicCallSite {
|
public final class DynamicCallSite {
|
||||||
|
|
||||||
|
private DynamicCallSite() {} // no instance!
|
||||||
|
|
||||||
// NOTE: these must be primitive types, see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokedynamic
|
// NOTE: these must be primitive types, see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokedynamic
|
||||||
/** static bootstrap parameter indicating a dynamic method call, e.g. foo.bar(...) */
|
/** static bootstrap parameter indicating a dynamic method call, e.g. foo.bar(...) */
|
||||||
public static final int METHOD_CALL = 0;
|
public static final int METHOD_CALL = 0;
|
||||||
|
@ -55,40 +57,24 @@ public final class DynamicCallSite {
|
||||||
/** static bootstrap parameter indicating a dynamic array store, e.g. foo[bar] = baz */
|
/** static bootstrap parameter indicating a dynamic array store, e.g. foo[bar] = baz */
|
||||||
public static final int ARRAY_STORE = 4;
|
public static final int ARRAY_STORE = 4;
|
||||||
|
|
||||||
static class InliningCacheCallSite extends MutableCallSite {
|
static final class InliningCacheCallSite extends MutableCallSite {
|
||||||
/** maximum number of types before we go megamorphic */
|
/** maximum number of types before we go megamorphic */
|
||||||
static final int MAX_DEPTH = 5;
|
static final int MAX_DEPTH = 5;
|
||||||
|
|
||||||
final Lookup lookup;
|
private final String name;
|
||||||
final String name;
|
private final int flavor;
|
||||||
final int flavor;
|
int depth; // pkg-protected for testing
|
||||||
int depth;
|
|
||||||
|
|
||||||
InliningCacheCallSite(Lookup lookup, String name, MethodType type, int flavor) {
|
InliningCacheCallSite(String name, MethodType type, int flavor) {
|
||||||
super(type);
|
super(type);
|
||||||
this.lookup = lookup;
|
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.flavor = flavor;
|
this.flavor = flavor;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
MethodHandle fallback = FALLBACK.bindTo(this);
|
||||||
* invokeDynamic bootstrap method
|
|
||||||
* <p>
|
|
||||||
* In addition to ordinary parameters, we also take a static parameter {@code flavor} which
|
|
||||||
* tells us what type of dynamic call it is (and which part of whitelist to look at).
|
|
||||||
* <p>
|
|
||||||
* see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokedynamic
|
|
||||||
*/
|
|
||||||
public static CallSite bootstrap(Lookup lookup, String name, MethodType type, int flavor) {
|
|
||||||
InliningCacheCallSite callSite = new InliningCacheCallSite(lookup, name, type, flavor);
|
|
||||||
|
|
||||||
MethodHandle fallback = FALLBACK.bindTo(callSite);
|
|
||||||
fallback = fallback.asCollector(Object[].class, type.parameterCount());
|
fallback = fallback.asCollector(Object[].class, type.parameterCount());
|
||||||
fallback = fallback.asType(type);
|
fallback = fallback.asType(type);
|
||||||
|
|
||||||
callSite.setTarget(fallback);
|
setTarget(fallback);
|
||||||
return callSite;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -122,41 +108,54 @@ public final class DynamicCallSite {
|
||||||
* Called when a new type is encountered (or, when we have encountered more than {@code MAX_DEPTH}
|
* Called when a new type is encountered (or, when we have encountered more than {@code MAX_DEPTH}
|
||||||
* types at this call site and given up on caching).
|
* types at this call site and given up on caching).
|
||||||
*/
|
*/
|
||||||
static Object fallback(InliningCacheCallSite callSite, Object[] args) throws Throwable {
|
Object fallback(Object[] args) throws Throwable {
|
||||||
MethodType type = callSite.type();
|
MethodType type = type();
|
||||||
Object receiver = args[0];
|
Object receiver = args[0];
|
||||||
Class<?> receiverClass = receiver.getClass();
|
Class<?> receiverClass = receiver.getClass();
|
||||||
MethodHandle target = lookup(callSite.flavor, receiverClass, callSite.name);
|
MethodHandle target = lookup(flavor, receiverClass, name);
|
||||||
target = target.asType(type);
|
target = target.asType(type);
|
||||||
|
|
||||||
if (callSite.depth >= InliningCacheCallSite.MAX_DEPTH) {
|
if (depth >= MAX_DEPTH) {
|
||||||
// revert to a vtable call
|
// revert to a vtable call
|
||||||
callSite.setTarget(target);
|
setTarget(target);
|
||||||
return target.invokeWithArguments(args);
|
return target.invokeWithArguments(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
MethodHandle test = CHECK_CLASS.bindTo(receiverClass);
|
MethodHandle test = CHECK_CLASS.bindTo(receiverClass);
|
||||||
test = test.asType(test.type().changeParameterType(0, type.parameterType(0)));
|
test = test.asType(test.type().changeParameterType(0, type.parameterType(0)));
|
||||||
|
|
||||||
MethodHandle guard = MethodHandles.guardWithTest(test, target, callSite.getTarget());
|
MethodHandle guard = MethodHandles.guardWithTest(test, target, getTarget());
|
||||||
callSite.depth++;
|
depth++;
|
||||||
|
|
||||||
callSite.setTarget(guard);
|
setTarget(guard);
|
||||||
return target.invokeWithArguments(args);
|
return target.invokeWithArguments(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final MethodHandle CHECK_CLASS;
|
private static final MethodHandle CHECK_CLASS;
|
||||||
private static final MethodHandle FALLBACK;
|
private static final MethodHandle FALLBACK;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
Lookup lookup = MethodHandles.lookup();
|
final Lookup lookup = MethodHandles.lookup();
|
||||||
try {
|
try {
|
||||||
CHECK_CLASS = lookup.findStatic(DynamicCallSite.class, "checkClass",
|
CHECK_CLASS = lookup.findStatic(lookup.lookupClass(), "checkClass",
|
||||||
MethodType.methodType(boolean.class, Class.class, Object.class));
|
MethodType.methodType(boolean.class, Class.class, Object.class));
|
||||||
FALLBACK = lookup.findStatic(DynamicCallSite.class, "fallback",
|
FALLBACK = lookup.findVirtual(lookup.lookupClass(), "fallback",
|
||||||
MethodType.methodType(Object.class, InliningCacheCallSite.class, Object[].class));
|
MethodType.methodType(Object.class, Object[].class));
|
||||||
} catch (ReflectiveOperationException e) {
|
} catch (ReflectiveOperationException e) {
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* invokeDynamic bootstrap method
|
||||||
|
* <p>
|
||||||
|
* In addition to ordinary parameters, we also take a static parameter {@code flavor} which
|
||||||
|
* tells us what type of dynamic call it is (and which part of whitelist to look at).
|
||||||
|
* <p>
|
||||||
|
* see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokedynamic
|
||||||
|
*/
|
||||||
|
public static CallSite bootstrap(Lookup lookup, String name, MethodType type, int flavor) {
|
||||||
|
return new InliningCacheCallSite(name, type, flavor);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue