Skip to content

Commit

Permalink
More improvement and refactoring of indy binding.
Browse files Browse the repository at this point in the history
* Install call site caching policies from JRuby 1.7.
* Clean up super sites by storing more in the site object.
* Add failure path to invocation sites.
* Remove switchpoint flag; we always use it now.
* Remove unused RubyInstanceConfig fields related to indy.
headius committed Oct 27, 2014
1 parent 088e62c commit 11205fa
Showing 13 changed files with 273 additions and 160 deletions.
44 changes: 0 additions & 44 deletions core/src/main/java/org/jruby/RubyInstanceConfig.java
Original file line number Diff line number Diff line change
@@ -1912,48 +1912,4 @@ public boolean isCextEnabled() {
return false;
}

/**
* In Java integration, allow upper case name for a Java package;
* e.g., com.example.UpperCase.Class
*/
@Deprecated
public static final boolean UPPER_CASE_PACKAGE_NAME_ALLOWED = Options.JI_UPPER_CASE_PACKAGE_NAME_ALLOWED.load();

@Deprecated public static final boolean USE_INVOKEDYNAMIC = Options.COMPILE_INVOKEDYNAMIC.load();

// max times an indy call site can fail before it goes to simple IC
@Deprecated public static final int MAX_FAIL_COUNT = Options.INVOKEDYNAMIC_MAXFAIL.load();

// max polymorphism at a call site to build a chained method handle PIC
@Deprecated public static final int MAX_POLY_COUNT = Options.INVOKEDYNAMIC_MAXPOLY.load();

// logging of various indy aspects
@Deprecated public static final boolean LOG_INDY_BINDINGS = Options.INVOKEDYNAMIC_LOG_BINDING.load();
@Deprecated public static final boolean LOG_INDY_CONSTANTS = Options.INVOKEDYNAMIC_LOG_CONSTANTS.load();

// properties enabling or disabling certain uses of invokedynamic
@Deprecated public static final boolean INVOKEDYNAMIC_ALL = USE_INVOKEDYNAMIC && Options.INVOKEDYNAMIC_ALL.load();
@Deprecated public static final boolean INVOKEDYNAMIC_SAFE = USE_INVOKEDYNAMIC && Options.INVOKEDYNAMIC_SAFE.load();

@Deprecated private static final boolean invokedynamicOn = INVOKEDYNAMIC_ALL || INVOKEDYNAMIC_SAFE || USE_INVOKEDYNAMIC;

@Deprecated public static final boolean INVOKEDYNAMIC_INVOCATION = invokedynamicOn && Options.INVOKEDYNAMIC_INVOCATION.load();

@Deprecated private static final boolean invokedynamicInvocation = invokedynamicOn && INVOKEDYNAMIC_INVOCATION;

@Deprecated public static final boolean INVOKEDYNAMIC_INVOCATION_SWITCHPOINT = invokedynamicInvocation && Options.INVOKEDYNAMIC_INVOCATION_SWITCHPOINT.load();
@Deprecated public static final boolean INVOKEDYNAMIC_INDIRECT = invokedynamicInvocation && Options.INVOKEDYNAMIC_INVOCATION_INDIRECT.load();
@Deprecated public static final boolean INVOKEDYNAMIC_JAVA = invokedynamicInvocation && Options.INVOKEDYNAMIC_INVOCATION_JAVA.load();
@Deprecated public static final boolean INVOKEDYNAMIC_ATTR = invokedynamicInvocation && Options.INVOKEDYNAMIC_INVOCATION_ATTR.load();
@Deprecated public static final boolean INVOKEDYNAMIC_FFI = invokedynamicInvocation && Options.INVOKEDYNAMIC_INVOCATION_FFI.load();
@Deprecated public static final boolean INVOKEDYNAMIC_FASTOPS = invokedynamicInvocation && Options.INVOKEDYNAMIC_INVOCATION_FASTOPS.load();

@Deprecated public static final boolean INVOKEDYNAMIC_CACHE = invokedynamicOn && Options.INVOKEDYNAMIC_CACHE.load();

@Deprecated private static final boolean invokedynamicCache = invokedynamicOn && INVOKEDYNAMIC_CACHE;

@Deprecated public static final boolean INVOKEDYNAMIC_CONSTANTS = invokedynamicCache && Options.INVOKEDYNAMIC_CACHE_CONSTANTS.load();
@Deprecated public static final boolean INVOKEDYNAMIC_LITERALS = invokedynamicCache&& Options.INVOKEDYNAMIC_CACHE_LITERALS.load();
@Deprecated public static final boolean INVOKEDYNAMIC_IVARS = invokedynamicCache&& Options.INVOKEDYNAMIC_CACHE_IVARS.load();

}
Original file line number Diff line number Diff line change
@@ -48,9 +48,14 @@ public InterpretedIRMethod(IRScope method, Visibility visibility, RubyModule imp
this.method = method;
this.method.getStaticScope().determineModule();
this.arity = calculateArity();
if (!implementationClass.getRuntime().getInstanceConfig().getCompileMode().shouldJIT()) {

// disable JIT for anything that's not an IRMethod, or if JIT is turned off
// FIXME: kinda hacky, but I use IRMethod data in JITCompiler, and module/class/script bodies generally only run once
if (!(method instanceof IRMethod) ||
!implementationClass.getRuntime().getInstanceConfig().getCompileMode().shouldJIT()) {
this.box.callCount = -1;
}

isSynthetic = method instanceof IRModuleBody;
}

Original file line number Diff line number Diff line change
@@ -12,13 +12,17 @@
* Created by headius on 10/23/14.
*/
public class ClassSuperInvokeSite extends SuperInvokeSite {
public ClassSuperInvokeSite(MethodType type, String name) {
super(type, name);
public ClassSuperInvokeSite(MethodType type, String name, String splatmapString) {
super(type, name, splatmapString);
}

public IRubyObject invoke(String methodName, boolean[] splatMap, ThreadContext context, IRubyObject caller, IRubyObject self, RubyClass definingModule, IRubyObject[] args, Block block) throws Throwable {
public IRubyObject invoke(ThreadContext context, IRubyObject caller, IRubyObject self, RubyClass definingModule, IRubyObject[] args, Block block) throws Throwable {
// TODO: get rid of caller
// TODO: caching
return IRRuntimeHelpers.classSuperSplatArgs(context, self, methodName, definingModule.getMetaClass(), args, block, splatMap);
}

public IRubyObject fail(ThreadContext context, IRubyObject caller, IRubyObject self, RubyClass definingModule, IRubyObject[] args, Block block) throws Throwable {
return invoke(context, caller, self, definingModule, args, block);
}
}
Original file line number Diff line number Diff line change
@@ -12,13 +12,17 @@
* Created by headius on 10/23/14.
*/
public class InstanceSuperInvokeSite extends SuperInvokeSite {
public InstanceSuperInvokeSite(MethodType type, String name) {
super(type, name);
public InstanceSuperInvokeSite(MethodType type, String name, String splatmapString) {
super(type, name, splatmapString);
}

public IRubyObject invoke(String methodName, boolean[] splatMap, ThreadContext context, IRubyObject caller, IRubyObject self, RubyClass definingModule, IRubyObject[] args, Block block) throws Throwable {
public IRubyObject invoke(ThreadContext context, IRubyObject caller, IRubyObject self, RubyClass definingModule, IRubyObject[] args, Block block) throws Throwable {
// TODO: get rid of caller
// TODO: caching
return IRRuntimeHelpers.instanceSuperSplatArgs(context, self, methodName, definingModule, args, block, splatMap);
}

public IRubyObject fail(ThreadContext context, IRubyObject caller, IRubyObject self, RubyClass definingModule, IRubyObject[] args, Block block) throws Throwable {
return invoke(context, caller, self, definingModule, args, block);
}
}
226 changes: 200 additions & 26 deletions core/src/main/java/org/jruby/ir/targets/InvokeSite.java
Original file line number Diff line number Diff line change
@@ -3,28 +3,58 @@
import com.headius.invokebinder.Binder;
import com.headius.invokebinder.Signature;
import com.headius.invokebinder.SmartBinder;
import com.headius.invokebinder.SmartHandle;
import org.jruby.RubyBasicObject;
import org.jruby.RubyBoolean;
import org.jruby.RubyClass;
import org.jruby.RubyFixnum;
import org.jruby.RubyFloat;
import org.jruby.RubyModule;
import org.jruby.RubyNil;
import org.jruby.RubySymbol;
import org.jruby.internal.runtime.methods.DynamicMethod;
import org.jruby.ir.JIT;
import org.jruby.runtime.Block;
import org.jruby.runtime.CallType;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.callsite.CacheEntry;
import org.jruby.runtime.invokedynamic.JRubyCallSite;
import org.jruby.util.cli.Options;
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;

import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.MutableCallSite;
import java.lang.invoke.SwitchPoint;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;

import static java.lang.invoke.MethodHandles.lookup;

/**
* Created by headius on 10/23/14.
*/
public class InvokeSite extends MutableCallSite {
public abstract class InvokeSite extends MutableCallSite {
final Signature signature;
final Signature fullSignature;
final int arity;
protected final String methodName;
final MethodHandle fallback;
private final Set<Integer> seenTypes = new HashSet<Integer>();
private int clearCount;
private static final AtomicLong SITE_ID = new AtomicLong(1);
private final long siteID = SITE_ID.getAndIncrement();
private final int argOffset;
private boolean boundOnce;
CacheEntry cache = CacheEntry.NULL_CACHE;

private static final Logger LOG = LoggerFactory.getLogger("InvokeSite");

public String name() {
return methodName;
@@ -38,7 +68,6 @@ public InvokeSite(MethodType type, String name, CallType callType) {
this.callType = callType;

Signature startSig;
int argOffset;

if (callType == CallType.SUPER) {
// super calls receive current class argument, so offsets and signature are different
@@ -79,40 +108,81 @@ public InvokeSite(MethodType type, String name, CallType callType) {
}

this.arity = arity;

this.fallback = prepareBinder().invokeVirtualQuiet(Bootstrap.LOOKUP, "invoke");
}

public static CallSite bootstrap(InvokeSite site, MethodHandles.Lookup lookup) {
MethodHandle handle;
site.setInitialTarget(site.fallback);

return site;
}

handle = site.prepareBinder().invokeVirtualQuiet(lookup, "invoke");
public IRubyObject invoke(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject[] args, Block block) throws Throwable {
RubyClass selfClass = pollAndGetClass(context, self);
SwitchPoint switchPoint = (SwitchPoint) selfClass.getInvalidator().getData();
CacheEntry entry = selfClass.searchWithCache(methodName);
DynamicMethod method = entry.method;

site.setTarget(handle);
if (methodMissing(entry, caller)) {
return callMethodMissing(entry, callType, context, self, methodName, args, block);
}

return site;
MethodHandle mh = getHandle(selfClass, this, method);

updateInvocationTarget(mh, self, selfClass, entry, switchPoint);

return method.call(context, self, selfClass, methodName, args, block);
}

/**
* Failover version uses a monomorphic cache and DynamicMethod.call, as in non-indy.
*/
public IRubyObject fail(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject[] args, Block block) throws Throwable {
RubyClass selfClass = pollAndGetClass(context, self);
String name = methodName;
CacheEntry entry = cache;

if (entry.typeOk(selfClass)) {
return entry.method.call(context, self, selfClass, name, args, block);
}

entry = selfClass.searchWithCache(name);

if (methodMissing(entry, caller)) {
return callMethodMissing(entry, callType, context, self, name, args, block);
}

cache = entry;

return entry.method.call(context, self, selfClass, name, args, block);
}

public Binder prepareBinder() {
SmartBinder binder = SmartBinder.from(signature);

binder = binder.insert(0, "site", this);

// prepare arg[]
if (arity == -1) {
// do nothing, already have IRubyObject[] in args
} else if (arity == 0) {
binder = binder.insert(4, "args", IRubyObject.NULL_ARRAY);
binder = binder.insert(argOffset, "args", IRubyObject.NULL_ARRAY);
} else {
binder = binder
.collect("args", "arg[0-9]+");
}

// add block if needed
if (signature.lastArgType() != Block.class) {
binder = binder.append("block", Block.NULL_BLOCK);
}

// bind to site
binder = binder.insert(0, "site", this);

return binder.binder();
}

MethodHandle getHandle(RubyClass selfClass, SwitchPoint switchPoint, InvokeSite site, DynamicMethod method) throws Throwable {
MethodHandle getHandle(RubyClass selfClass, InvokeSite site, DynamicMethod method) throws Throwable {
boolean blockGiven = signature.lastArgType() == Block.class;

MethodHandle mh = Bootstrap.buildNativeHandle(site, method, blockGiven);
@@ -121,26 +191,130 @@ MethodHandle getHandle(RubyClass selfClass, SwitchPoint switchPoint, InvokeSite

assert mh != null : "we should have a method handle of some sort by now";

MethodHandle fallback;
SmartBinder fallbackBinder = SmartBinder
.from(site.signature);
return mh;
}

/**
* Update the given call site using the new target, wrapping with appropriate
* guard and argument-juggling logic. Return a handle suitable for invoking
* with the site's original method type.
*/
MethodHandle updateInvocationTarget(MethodHandle target, IRubyObject self, RubyModule selfClass, CacheEntry entry, SwitchPoint switchPoint) {
if (target == null ||
clearCount > Options.INVOKEDYNAMIC_MAXFAIL.load() ||
(!hasSeenType(selfClass.id)
&& seenTypesCount() + 1 > Options.INVOKEDYNAMIC_MAXPOLY.load())) {
setTarget(target = prepareBinder().invokeVirtualQuiet(lookup(), "fail"));
} else {
MethodHandle fallback;
MethodHandle gwt;

// if we've cached no types, and the site is bound and we haven't seen this new type...
if (seenTypesCount() > 0 && getTarget() != null && !hasSeenType(selfClass.id)) {
// stack it up into a PIC
if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) LOG.info(methodName + "\tadded to PIC " + logMethod(entry.method));
fallback = getTarget();
} else {
// wipe out site with this new type and method
String bind = boundOnce ? "rebind" : "bind";
if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) LOG.info(methodName + "\ttriggered site #" + siteID + " " + bind);// + " (" + file() + ":" + line() + ")");
fallback = this.fallback;
clearTypes();
}

// fallbacks only up to arity 3
if (site.arity > 3) fallbackBinder = fallbackBinder.collect("args", "arg.*");
addType(selfClass.id);

// insert site and bind to target method
fallback = prepareBinder()
.invokeVirtual(Bootstrap.LOOKUP, "invoke");
SmartHandle test;
SmartBinder selfTest = SmartBinder
.from(signature.asFold(boolean.class))
.permute("self");

MethodHandle test = SmartBinder
.from(site.signature.changeReturn(boolean.class))
.permute("self")
.insert(0, "selfClass", RubyClass.class, selfClass)
.invokeStatic(Bootstrap.LOOKUP, Bootstrap.class, "testType").handle();
if (self instanceof RubySymbol ||
self instanceof RubyFixnum ||
self instanceof RubyFloat ||
self instanceof RubyNil ||
self instanceof RubyBoolean.True ||
self instanceof RubyBoolean.False) {

mh = MethodHandles.guardWithTest(test, mh, fallback);
mh = switchPoint.guardWithTest(mh, fallback);
test = selfTest
.insert(1, "selfJavaType", self.getClass())
.cast(boolean.class, Object.class, Class.class)
.invoke(TEST_CLASS);

return mh;
} else {

test = SmartBinder
.from(signature.changeReturn(boolean.class))
.permute("self")
.insert(0, "selfClass", RubyClass.class, selfClass)
.invokeStaticQuiet(Bootstrap.LOOKUP, Bootstrap.class, "testType");
}

gwt = MethodHandles.guardWithTest(test.handle(), target, fallback);

// wrap in switchpoint for mutation invalidation
gwt = switchPoint.guardWithTest(gwt, fallback);

setTarget(gwt);
}

return target;
}

public RubyClass pollAndGetClass(ThreadContext context, IRubyObject self) {
context.callThreadPoll();
RubyClass selfType = ((RubyBasicObject)self).getMetaClass();
return selfType;
}

@Override
public void setTarget(MethodHandle target) {
super.setTarget(target);
boundOnce = true;
}

public void setInitialTarget(MethodHandle target) {
super.setTarget(target);
}

public synchronized boolean hasSeenType(int typeCode) {
return seenTypes.contains(typeCode);
}

public synchronized void addType(int typeCode) {
seenTypes.add(typeCode);
}

public synchronized int seenTypesCount() {
return seenTypes.size();
}

public synchronized void clearTypes() {
seenTypes.clear();
clearCount++;
}

public abstract boolean methodMissing(CacheEntry entry, IRubyObject caller);

public IRubyObject callMethodMissing(CacheEntry entry, CallType callType, ThreadContext context, IRubyObject self, String name, IRubyObject[] args, Block block) {
return Helpers.selectMethodMissing(context, self, entry.method.getVisibility(), name, callType).call(context, self, self.getMetaClass(), name, args, block);
}

private static String logMethod(DynamicMethod method) {
return "[#" + method.getSerialNumber() + " " + method.getImplementationClass() + "]";
}

@JIT
public static boolean testMetaclass(RubyClass metaclass, IRubyObject self) {
return metaclass == ((RubyBasicObject)self).getMetaClass();
}

@JIT
public static boolean testClass(Object object, Class clazz) {
return object.getClass() == clazz;
}

private static final MethodHandle TEST_CLASS = Binder
.from(boolean.class, Object.class, Class.class)
.invokeStaticQuiet(lookup(), InvokeSite.class, "testClass");
}
19 changes: 5 additions & 14 deletions core/src/main/java/org/jruby/ir/targets/NormalInvokeSite.java
Original file line number Diff line number Diff line change
@@ -26,6 +26,8 @@
* Created by headius on 10/23/14.
*/
public class NormalInvokeSite extends InvokeSite {
CacheEntry cache;

public NormalInvokeSite(MethodType type, String name) {
super(type, name, CallType.NORMAL);
}
@@ -38,21 +40,10 @@ public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, Metho
return InvokeSite.bootstrap(site, lookup);
}

public IRubyObject invoke(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject[] args, Block block) throws Throwable {
RubyClass selfClass = self.getMetaClass();

SwitchPoint switchPoint = (SwitchPoint)selfClass.getInvalidator().getData();
CacheEntry entry = selfClass.searchWithCache(methodName);
@Override
public boolean methodMissing(CacheEntry entry, IRubyObject caller) {
DynamicMethod method = entry.method;

if (methodMissing(entry, CallType.NORMAL, methodName, caller)) {
return callMethodMissing(entry, CallType.NORMAL, context, self, methodName, args, block);
}

MethodHandle mh = getHandle(selfClass, switchPoint, this, method);

this.setTarget(mh);
// FIXME: this varargs path needs to splat to call against handle
return method.call(context, self, selfClass, methodName, args, block);
return method.isUndefined() || (!methodName.equals("method_missing") && !method.isCallableFrom(caller, callType));
}
}
21 changes: 8 additions & 13 deletions core/src/main/java/org/jruby/ir/targets/SelfInvokeSite.java
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
import org.jruby.internal.runtime.methods.DynamicMethod;
import org.jruby.runtime.Block;
import org.jruby.runtime.CallType;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.callsite.CacheEntry;
@@ -30,6 +31,10 @@ public SelfInvokeSite(MethodType type, String name) {
super(type, name, CallType.FUNCTIONAL);
}

public SelfInvokeSite(MethodType type, String name, CallType callType) {
super(type, name, callType);
}

public static Handle BOOTSTRAP = new Handle(Opcodes.H_INVOKESTATIC, p(SelfInvokeSite.class), "bootstrap", sig(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class));

public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, MethodType type) {
@@ -38,20 +43,10 @@ public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, Metho
return InvokeSite.bootstrap(site, lookup);
}

public IRubyObject invoke(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject[] args, Block block) throws Throwable {
RubyClass selfClass = self.getMetaClass();
SwitchPoint switchPoint = (SwitchPoint) selfClass.getInvalidator().getData();
CacheEntry entry = selfClass.searchWithCache(methodName);
@Override
public boolean methodMissing(CacheEntry entry, IRubyObject caller) {
DynamicMethod method = entry.method;

if (methodMissing(entry, CallType.FUNCTIONAL, methodName, caller)) {
return callMethodMissing(entry, CallType.FUNCTIONAL, context, self, methodName, args, block);
}

MethodHandle mh = getHandle(selfClass, switchPoint, this, method);

setTarget(mh);

return method.call(context, self, selfClass, methodName, args, block);
return method.isUndefined();
}
}
48 changes: 19 additions & 29 deletions core/src/main/java/org/jruby/ir/targets/SuperInvokeSite.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package org.jruby.ir.targets;

import com.headius.invokebinder.Binder;
import com.headius.invokebinder.SmartBinder;
import org.jruby.RubyClass;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.runtime.Block;
import org.jruby.runtime.CallType;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.callsite.CacheEntry;
import org.jruby.util.JavaNameMangler;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Opcodes;
@@ -23,9 +25,15 @@
/**
* Created by headius on 10/23/14.
*/
public abstract class SuperInvokeSite extends InvokeSite {
public SuperInvokeSite(MethodType type, String name) {
super(type, name, CallType.SUPER);
public abstract class SuperInvokeSite extends SelfInvokeSite {
protected final String superName;
protected final boolean[] splatMap;

public SuperInvokeSite(MethodType type, String superName, String splatmapString) {
super(type, superName, CallType.SUPER);

this.superName = superName;
this.splatMap = IRRuntimeHelpers.decodeSplatmap(splatmapString);
}

public static final Handle BOOTSTRAP = new Handle(Opcodes.H_INVOKESTATIC, p(SuperInvokeSite.class), "bootstrap", sig(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, String.class));
@@ -38,43 +46,25 @@ public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, Metho

switch (targetAndMethod[0]) {
case "invokeInstanceSuper":
site = new InstanceSuperInvokeSite(type, name);
site = new InstanceSuperInvokeSite(type, superName, splatmapString);
break;
case "invokeClassSuper":
site = new ClassSuperInvokeSite(type, name);
site = new ClassSuperInvokeSite(type, superName, splatmapString);
break;
case "invokeUnresolvedSuper":
site = new UnresolvedSuperInvokeSite(type, name);
site = new UnresolvedSuperInvokeSite(type, superName, splatmapString);
break;
case "invokeZSuper":
site = new ZSuperInvokeSite(type, name);
site = new ZSuperInvokeSite(type, superName, splatmapString);
break;
default:
throw new RuntimeException("invalid super call: " + name);
}

MethodHandle handle;

boolean[] splatMap = IRRuntimeHelpers.decodeSplatmap(splatmapString);

SmartBinder binder = SmartBinder.from(site.signature)
.insert(
0,
arrayOf("site", "name", "splatMap"),
arrayOf(SuperInvokeSite.class, String.class, boolean[].class),
site, superName, splatMap);

if (site.arity > 0) {
binder = binder
.collect("args", "arg[0-9]+");
}

handle = binder.invokeVirtualQuiet(lookup, "invoke").handle();

site.setTarget(handle);

return site;
return InvokeSite.bootstrap(site, lookup);
}

public abstract IRubyObject invoke(String methodName, boolean[] splatMap, ThreadContext context, IRubyObject caller, IRubyObject self, RubyClass definingModule, IRubyObject[] args, Block block) throws Throwable;
public abstract IRubyObject invoke(ThreadContext context, IRubyObject caller, IRubyObject self, RubyClass definingModule, IRubyObject[] args, Block block) throws Throwable;

public abstract IRubyObject fail(ThreadContext context, IRubyObject caller, IRubyObject self, RubyClass definingModule, IRubyObject[] args, Block block) throws Throwable;
}
Original file line number Diff line number Diff line change
@@ -12,13 +12,17 @@
* Created by headius on 10/23/14.
*/
public class UnresolvedSuperInvokeSite extends SuperInvokeSite {
public UnresolvedSuperInvokeSite(MethodType type, String name) {
super(type, name);
public UnresolvedSuperInvokeSite(MethodType type, String name, String splatmapString) {
super(type, name, splatmapString);
}

public IRubyObject invoke(String methodName, boolean[] splatMap, ThreadContext context, IRubyObject caller, IRubyObject self, RubyClass definingModule, IRubyObject[] args, Block block) throws Throwable {
public IRubyObject invoke(ThreadContext context, IRubyObject caller, IRubyObject self, RubyClass definingModule, IRubyObject[] args, Block block) throws Throwable {
// TODO: get rid of caller
// TODO: caching
return IRRuntimeHelpers.unresolvedSuperSplatArgs(context, self, args, block, splatMap);
}

public IRubyObject fail(ThreadContext context, IRubyObject caller, IRubyObject self, RubyClass definingModule, IRubyObject[] args, Block block) throws Throwable {
return invoke(context, caller, self, definingModule, args, block);
}
}
10 changes: 7 additions & 3 deletions core/src/main/java/org/jruby/ir/targets/ZSuperInvokeSite.java
Original file line number Diff line number Diff line change
@@ -12,14 +12,18 @@
* Created by headius on 10/23/14.
*/
public class ZSuperInvokeSite extends SuperInvokeSite {
public ZSuperInvokeSite(MethodType type, String name) {
super(type, name);
public ZSuperInvokeSite(MethodType type, String name, String splatmapString) {
super(type, name, splatmapString);
}

public IRubyObject invoke(String methodName, boolean[] splatMap, ThreadContext context, IRubyObject caller, IRubyObject self, RubyClass definingModule, IRubyObject[] args, Block block) throws Throwable {
public IRubyObject invoke(ThreadContext context, IRubyObject caller, IRubyObject self, RubyClass definingModule, IRubyObject[] args, Block block) throws Throwable {
// TODO: get rid of caller
// TODO: caching
if (block == null || !block.isGiven()) block = context.getFrameBlock();
return IRRuntimeHelpers.unresolvedSuperSplatArgs(context, self, args, block, splatMap);
}

public IRubyObject fail(ThreadContext context, IRubyObject caller, IRubyObject self, RubyClass definingModule, IRubyObject[] args, Block block) throws Throwable {
return invoke(context, caller, self, definingModule, args, block);
}
}
Original file line number Diff line number Diff line change
@@ -363,12 +363,8 @@ private static MethodHandle updateInvocationTarget(MethodHandle target, JRubyCal
.invoke(TEST_CLASS);

} else {

if (Options.INVOKEDYNAMIC_INVOCATION_SWITCHPOINT.load()) {
selfTest = selfTest.insert(0, "selfClass", selfClass);
} else {
selfTest = selfTest.insert(0, "token", entry.token);
}

selfTest = selfTest.insert(0, "selfClass", selfClass);

test = selfTest
.cast(boolean.class, RubyClass.class, IRubyObject.class)
@@ -377,11 +373,9 @@ private static MethodHandle updateInvocationTarget(MethodHandle target, JRubyCal

gwt = createGWT(test, target, fallback, entry, site, curry);

if (Options.INVOKEDYNAMIC_INVOCATION_SWITCHPOINT.load()) {
// wrap in switchpoint for mutation invalidation
gwt = switchPoint.guardWithTest(gwt, curry ? insertArguments(fallback, 0, site) : fallback);
}

// wrap in switchpoint for mutation invalidation
gwt = switchPoint.guardWithTest(gwt, curry ? insertArguments(fallback, 0, site) : fallback);

site.setTarget(gwt);
}

@@ -1916,10 +1910,7 @@ private static String logMethod(DynamicMethod method) {
.from(boolean.class, RubyClass.class, IRubyObject.class)
.invokeStaticQuiet(lookup(), InvocationLinker.class, "testMetaclass");

private static final MethodHandle TEST =
Options.INVOKEDYNAMIC_INVOCATION_SWITCHPOINT.load() ?
TEST_METACLASS :
TEST_GENERATION;
private static final MethodHandle TEST = TEST_METACLASS;

private static final MethodHandle TEST_CLASS = Binder
.from(boolean.class, Object.class, Class.class)
6 changes: 1 addition & 5 deletions core/src/main/java/org/jruby/runtime/opto/OptoFactory.java
Original file line number Diff line number Diff line change
@@ -81,7 +81,7 @@ public static Invalidator newGlobalInvalidator(int maxFailures) {
}

public static Invalidator newMethodInvalidator(RubyModule module) {
if (indyEnabled() && indyInvocationSwitchpoint()) {
if (indyEnabled()) {
try {
return new GenerationAndSwitchPointInvalidator(module);
} catch (Error e) {
@@ -98,10 +98,6 @@ private static Boolean indyConstants() {
return Options.INVOKEDYNAMIC_CACHE_CONSTANTS.load();
}

private static Boolean indyInvocationSwitchpoint() {
return Options.INVOKEDYNAMIC_INVOCATION_SWITCHPOINT.load();
}

private static void disableIndy() {
Options.COMPILE_INVOKEDYNAMIC.force("false");
}
1 change: 0 additions & 1 deletion core/src/main/java/org/jruby/util/cli/Options.java
Original file line number Diff line number Diff line change
@@ -88,7 +88,6 @@ public class Options {
public static final Option<Boolean> INVOKEDYNAMIC_ALL = bool(INVOKEDYNAMIC, "invokedynamic.all", false, "Enable all possible uses of invokedynamic.");
public static final Option<Boolean> INVOKEDYNAMIC_SAFE = bool(INVOKEDYNAMIC, "invokedynamic.safe", false, "Enable all safe (but maybe not fast) uses of invokedynamic.");
public static final Option<Boolean> INVOKEDYNAMIC_INVOCATION = bool(INVOKEDYNAMIC, "invokedynamic.invocation", true, "Enable invokedynamic for method invocations.");
public static final Option<Boolean> INVOKEDYNAMIC_INVOCATION_SWITCHPOINT = bool(INVOKEDYNAMIC, "invokedynamic.invocation.switchpoint", true, "Use SwitchPoint for class modification guards on invocations.");
public static final Option<Boolean> INVOKEDYNAMIC_INVOCATION_INDIRECT = bool(INVOKEDYNAMIC, "invokedynamic.invocation.indirect", true, "Also bind indirect method invokers to invokedynamic.");
public static final Option<Boolean> INVOKEDYNAMIC_INVOCATION_JAVA = bool(INVOKEDYNAMIC, "invokedynamic.invocation.java", true, "Bind Ruby to Java invocations with invokedynamic.");
public static final Option<Boolean> INVOKEDYNAMIC_INVOCATION_ATTR = bool(INVOKEDYNAMIC, "invokedynamic.invocation.attr", true, "Bind Ruby attribute invocations directly to invokedynamic.");

0 comments on commit 11205fa

Please sign in to comment.