Permalink
Browse files

Add support for get/set of ivars via invokedynamic. Works, but disabl…

…ed for now.
  • Loading branch information...
headius committed Nov 29, 2011
1 parent 1dd08ac commit 32c559b56306411e8c41d6e87304e9478ff59710
@@ -1528,6 +1528,8 @@ public boolean shouldPrecompileAll() {
USE_INVOKEDYNAMIC && INVOKEDYNAMIC_CACHE && Options.INVOKEDYNAMIC_CACHE_CONSTANTS.load();
public static final boolean INVOKEDYNAMIC_LITERALS = INVOKEDYNAMIC_ALL || INVOKEDYNAMIC_SAFE ||
USE_INVOKEDYNAMIC && INVOKEDYNAMIC_CACHE && Options.INVOKEDYNAMIC_CACHE_LITERALS.load();
+ public static final boolean INVOKEDYNAMIC_IVARS = INVOKEDYNAMIC_ALL || INVOKEDYNAMIC_SAFE ||
+ USE_INVOKEDYNAMIC && INVOKEDYNAMIC_CACHE && Options.INVOKEDYNAMIC_CACHE_LITERALS.load();
// properties for logging exceptions, backtraces, and caller invocations
public static final boolean LOG_EXCEPTIONS = Options.LOG_EXCEPTIONS.load();
@@ -38,6 +38,7 @@
import org.jruby.ast.NodeType;
import org.jruby.ast.executable.AbstractScript;
import org.jruby.compiler.ASTInspector;
+import org.jruby.compiler.CompilerCallback;
import org.jruby.javasupport.util.RuntimeHelpers;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.BlockBody;
@@ -398,4 +399,33 @@ public void cacheSymbol(BaseBodyCompiler method, String symbol) {
InvokeDynamicSupport.getSymbolHandle(),
symbol);
}
+
+ public void cachedGetVariable(BaseBodyCompiler method, String name) {
+ if (!RubyInstanceConfig.INVOKEDYNAMIC_IVARS) {
+ super.cachedGetVariable(method, name);
+ return;
+ }
+
+ method.loadSelf();
+
+ method.method.invokedynamic(
+ "get:" + name,
+ sig(IRubyObject.class, IRubyObject.class),
+ InvokeDynamicSupport.getVariableHandle());
+ }
+
+ public void cachedSetVariable(BaseBodyCompiler method, String name, CompilerCallback valueCallback) {
+ if (!RubyInstanceConfig.INVOKEDYNAMIC_IVARS) {
+ super.cachedSetVariable(method, name, valueCallback);
+ return;
+ }
+
+ method.loadSelf();
+ valueCallback.call(method);
+
+ method.method.invokedynamic(
+ "set:" + name,
+ sig(IRubyObject.class, IRubyObject.class, IRubyObject.class),
+ InvokeDynamicSupport.getVariableHandle());
+ }
}
@@ -702,6 +702,10 @@ public static IRubyObject nullToNil(IRubyObject value, Ruby runtime) {
return value != null ? value : runtime.getNil();
}
+ public static IRubyObject nullToNil(IRubyObject value, IRubyObject nil) {
+ return value != null ? value : nil;
+ }
+
public static RubyClass prepareSuperClass(Ruby runtime, IRubyObject rubyClass) {
RubyClass.checkInheritable(rubyClass); // use the same logic as in EvaluationState
return (RubyClass)rubyClass;
@@ -557,6 +557,10 @@ public static boolean testGeneration(int token, IRubyObject self) {
public static boolean testMetaclass(RubyModule metaclass, IRubyObject self) {
return metaclass == ((RubyBasicObject)self).getMetaClass();
}
+
+ public static boolean testRealClass(RubyClass realclass, IRubyObject self) {
+ return realclass == ((RubyBasicObject)self).getMetaClass().getRealClass();
+ }
public static IRubyObject getLast(IRubyObject[] args) {
return args[args.length - 1];
@@ -165,6 +165,10 @@ public static Handle getFloatOperatorHandle() {
return getBootstrapHandle("floatOperatorBootstrap", BOOTSTRAP_STRING_DOUBLE_SIG);
}
+ public static Handle getVariableHandle() {
+ return getBootstrapHandle("variableBootstrap", BOOTSTRAP_BARE_SIG);
+ }
+
////////////////////////////////////////////////////////////////////////////
// BOOTSTRAP METHODS
////////////////////////////////////////////////////////////////////////////
@@ -395,6 +399,90 @@ public static CallSite getBlockBody19Bootstrap(Lookup lookup, String name, Metho
site.setTarget(init);
return site;
}
+
+ public static class VariableSite extends MutableCallSite {
+ public final String name;
+ public VariableSite(MethodType type, String name) {
+ super(type);
+ this.name = name;
+ }
+ }
+
+ public static CallSite variableBootstrap(Lookup lookup, String name, MethodType type) throws Throwable {
+ String varName = name.substring(4);
+ VariableSite site = new VariableSite(type, varName);
+ MethodHandle handle;
+
+ if (name.startsWith("get:")) {
+ handle = lookup.findStatic(InvokeDynamicSupport.class, "getVariableFallback", methodType(IRubyObject.class, VariableSite.class, IRubyObject.class));
+ } else if (name.startsWith("set:")) {
+ handle = lookup.findStatic(InvokeDynamicSupport.class, "setVariableFallback", methodType(IRubyObject.class, VariableSite.class, IRubyObject.class, IRubyObject.class));
+ } else {
+ throw new RuntimeException("invalid variable access type");
+ }
+
+ handle = handle.bindTo(site);
+ site.setTarget(handle);
+
+ return site;
+ }
+
+ public static IRubyObject getVariableFallback(VariableSite site, IRubyObject self) throws Throwable {
+ RubyClass.VariableAccessor accessor = self.getMetaClass().getRealClass().getVariableAccessorForRead(site.name);
+
+ // produce nil if the variable has not been initialize
+ MethodHandle nullToNil = findStatic(RuntimeHelpers.class, "nullToNil", methodType(IRubyObject.class, IRubyObject.class, IRubyObject.class));
+ nullToNil = insertArguments(nullToNil, 1, self.getRuntime().getNil());
+ nullToNil = explicitCastArguments(nullToNil, methodType(IRubyObject.class, Object.class));
+
+ // get variable value and filter with nullToNil
+ MethodHandle getValue = findVirtual(IRubyObject.class, "getVariable", methodType(Object.class, int.class));
+ getValue = insertArguments(getValue, 1, accessor.getIndex());
+ getValue = filterReturnValue(getValue, nullToNil);
+
+ // prepare fallback
+ MethodHandle fallback = findStatic(InvokeDynamicSupport.class, "getVariableFallback", methodType(IRubyObject.class, VariableSite.class, IRubyObject.class));
+ fallback = fallback.bindTo(site);
+
+ // prepare test
+ MethodHandle test = findStatic(InvocationLinker.class, "testRealClass", methodType(boolean.class, RubyClass.class, IRubyObject.class));
+ test = test.bindTo(self.getMetaClass().getRealClass());
+
+ getValue = guardWithTest(test, getValue, fallback);
+
+ site.setTarget(getValue);
+
+ return (IRubyObject)getValue.invokeWithArguments(self);
+ }
+
+ public static IRubyObject setVariableFallback(VariableSite site, IRubyObject self, IRubyObject value) throws Throwable {
+ RubyClass.VariableAccessor accessor = self.getMetaClass().getRealClass().getVariableAccessorForWrite(site.name);
+
+ // return provided value
+ MethodHandle returnValue = identity(IRubyObject.class);
+ returnValue = dropArguments(returnValue, 0, IRubyObject.class);
+
+ // set variable value and fold by returning value
+ MethodHandle setValue = findVirtual(IRubyObject.class, "setVariable", methodType(void.class, int.class, Object.class));
+ setValue = explicitCastArguments(setValue, methodType(void.class, IRubyObject.class, int.class, IRubyObject.class));
+ setValue = insertArguments(setValue, 1, accessor.getIndex());
+ setValue = foldArguments(returnValue, setValue);
+
+ // prepare fallback
+ MethodHandle fallback = findStatic(InvokeDynamicSupport.class, "setVariableFallback", methodType(IRubyObject.class, VariableSite.class, IRubyObject.class, IRubyObject.class));
+ fallback = fallback.bindTo(site);
+
+ // prepare test
+ MethodHandle test = findStatic(InvocationLinker.class, "testRealClass", methodType(boolean.class, RubyClass.class, IRubyObject.class));
+ test = test.bindTo(self.getMetaClass().getRealClass());
+ test = dropArguments(test, 1, IRubyObject.class);
+
+ setValue = guardWithTest(test, setValue, fallback);
+
+ site.setTarget(setValue);
+
+ return (IRubyObject)setValue.invokeWithArguments(self, value);
+ }
////////////////////////////////////////////////////////////////////////////
// INITIAL AND FALLBACK METHODS FOR POST BOOTSTRAP
@@ -74,6 +74,7 @@
public static final Option<Boolean> INVOKEDYNAMIC_CACHE = bool(INVOKEDYNAMIC, "invokedynamic.cache", true, "Use invokedynamic to load cached values like literals and constants.");
public static final Option<Boolean> INVOKEDYNAMIC_CACHE_CONSTANTS = bool(INVOKEDYNAMIC, "invokedynamic.cache.constants", true, "Use invokedynamic to load constants.");
public static final Option<Boolean> INVOKEDYNAMIC_CACHE_LITERALS = bool(INVOKEDYNAMIC, "invokedynamic.cache.literals", true, "Use invokedynamic to load literals.");
+ public static final Option<Boolean> INVOKEDYNAMIC_CACHE_IVARS = bool(INVOKEDYNAMIC, "invokedynamic.cache.literals", false, "Use invokedynamic to get/set instance variables.");
public static final Option<Integer> JIT_THRESHOLD = integer(JIT, "jit.threshold", Constants.JIT_THRESHOLD, "Set the JIT threshold to the specified method invocation count.");
public static final Option<Integer> JIT_MAX = integer(JIT, "jit.max", Constants.JIT_MAX_METHODS_LIMIT, "Set the max count of active methods eligible for JIT-compilation.");

0 comments on commit 32c559b

Please sign in to comment.