Merge pull request #18323 from uschindler/dynamiccallsite_cleanup

painless: cleanup of DynamicCallSite
This commit is contained in:
Robert Muir 2016-05-13 11:19:27 -04:00
commit 520697eb14
1 changed files with 83 additions and 84 deletions

View File

@ -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);
}
}