Skip to content

Commit

Permalink
Dirt simple fixed-arity support in new JIT.
Browse files Browse the repository at this point in the history
In order to bring the new JIT in line with the old JIT in terms of
performance, this commit makes the JIT emit a second version of a
given method with exactly the number of required arguments rather
than just an array-boxed version. This second handle is used by
CompiledIRMethod and Bootstrap to wire matching-arity calls
straight through to the right target.

This brings the performance of several benchmarks within throwing
distance of the 1.7 JIT.

Caveats:

* No checking for maximum supported arity. We try to patch through
  any number of arguments. This is good, because it means we can
  escape the 0-3 arity splitting for Ruby code, but it also means
  we're missing some verification that there's not too many args
  to handle in the intermediate MethodHandles.
* CompiledIRMethod.call now has overloads for other arities, but
  this is the only benefit to JVM6 support right now. JVM6 will
  need to emit DynamicMethod.call invocations that match 0-3 args
  rather than boxing all the time.
* This is not as generic as I'd like it to be to support multiple
  method entry points. The approach may extend well to methods
  with N..M arguments.

I have only done a bit of testing with this, but many different
fixed-arity and variable-arity methods appear to bind correctly.
headius committed Oct 23, 2014
1 parent fa44bc1 commit 31f7926
Showing 12 changed files with 283 additions and 72 deletions.
Original file line number Diff line number Diff line change
@@ -4,16 +4,13 @@
import org.jruby.ir.IRFlags;
import org.jruby.ir.IRMetaClassBody;
import org.jruby.ir.IRScope;
import org.jruby.ir.interpreter.InterpreterContext;
import org.jruby.runtime.Block;
import org.jruby.runtime.DynamicScope;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;

import java.lang.invoke.MethodHandle;
import java.util.ArrayList;
import java.util.List;

public class CompiledIRMetaClassBody extends CompiledIRMethod {
private final IRMetaClassBody irMetaClassBody;
@@ -58,7 +55,7 @@ protected void pre(ThreadContext context, IRubyObject self, String name, Block b

@Override
public DynamicMethod dup() {
CompiledIRMetaClassBody x = new CompiledIRMetaClassBody(handle, method, implementationClass);
CompiledIRMetaClassBody x = new CompiledIRMetaClassBody(variable, method, implementationClass);

return x;
}
Original file line number Diff line number Diff line change
@@ -3,7 +3,6 @@
import org.jruby.RubyModule;
import org.jruby.ir.IRMethod;
import org.jruby.ir.IRScope;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.Arity;
import org.jruby.runtime.Block;
@@ -15,28 +14,35 @@
import org.jruby.util.log.LoggerFactory;

import java.lang.invoke.MethodHandle;
import java.util.ArrayList;
import java.util.List;

import org.jruby.runtime.Helpers;

public class CompiledIRMethod extends JavaMethod implements MethodArgs2, PositionAware {
private static final Logger LOG = LoggerFactory.getLogger("CompiledIRMethod");

protected final MethodHandle handle;
protected final MethodHandle variable;

protected final MethodHandle specific;
protected final int specificArity;

protected final IRScope method;
private final Arity arity;
private String[] parameterList;

public CompiledIRMethod(MethodHandle handle, IRScope method, Visibility visibility, RubyModule implementationClass) {
public CompiledIRMethod(MethodHandle variable, IRScope method, Visibility visibility, RubyModule implementationClass) {
this(variable, null, -1, method, visibility, implementationClass);
}

public CompiledIRMethod(MethodHandle variable, MethodHandle specific, int specificArity, IRScope method, Visibility visibility, RubyModule implementationClass) {
super(implementationClass, visibility, CallConfiguration.FrameNoneScopeNone, method.getName());
this.handle = handle;
this.variable = variable;
this.specific = specific;
this.specificArity = specificArity;
this.method = method;
this.method.getStaticScope().determineModule();
this.arity = calculateArity();

setHandle(handle);
setHandle(variable);
}

public IRScope getIRMethod() {
@@ -47,6 +53,14 @@ public StaticScope getStaticScope() {
return method.getStaticScope();
}

public MethodHandle getHandleFor(int arity) {
if (specificArity != -1 && arity == specificArity) {
return specific;
}

return null;
}

public String[] getParameterList() {
if (parameterList != null) return parameterList;

@@ -89,7 +103,71 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
pre(context, self, name, block);

try {
return (IRubyObject)this.handle.invokeExact(context, method.getStaticScope(), self, args, block, implementationClass);
return (IRubyObject)this.variable.invokeExact(context, method.getStaticScope(), self, args, block, implementationClass);
} catch (Throwable t) {
Helpers.throwException(t);
// not reached
return null;
} finally {
post(context);
}
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
if (specificArity != 0) return call(context, self, clazz, name, IRubyObject.NULL_ARRAY, block);
pre(context, self, name, block);

try {
return (IRubyObject)this.specific.invokeExact(context, method.getStaticScope(), self, block, implementationClass);
} catch (Throwable t) {
Helpers.throwException(t);
// not reached
return null;
} finally {
post(context);
}
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, Block block) {
if (specificArity != 1) return call(context, self, clazz, name, Helpers.arrayOf(arg0), block);
pre(context, self, name, block);

try {
return (IRubyObject)this.specific.invokeExact(context, method.getStaticScope(), self, arg0, block, implementationClass);
} catch (Throwable t) {
Helpers.throwException(t);
// not reached
return null;
} finally {
post(context);
}
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, Block block) {
if (specificArity != 2) return call(context, self, clazz, name, Helpers.arrayOf(arg0, arg1), block);
pre(context, self, name, block);

try {
return (IRubyObject)this.specific.invokeExact(context, method.getStaticScope(), self, arg0, arg1, block, implementationClass);
} catch (Throwable t) {
Helpers.throwException(t);
// not reached
return null;
} finally {
post(context);
}
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
if (specificArity != 3) return call(context, self, clazz, name, Helpers.arrayOf(arg0, arg1, arg2), block);
pre(context, self, name, block);

try {
return (IRubyObject)this.specific.invokeExact(context, method.getStaticScope(), self, arg0, arg1, arg2, block, implementationClass);
} catch (Throwable t) {
Helpers.throwException(t);
// not reached
@@ -105,7 +183,7 @@ public boolean hasExplicitCallProtocol() {

@Override
public DynamicMethod dup() {
return new CompiledIRMethod(handle, method, visibility, implementationClass);
return new CompiledIRMethod(variable, specific, specificArity, method, visibility, implementationClass);
}

public String getFile() {
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/ir/IRManager.java
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@
public class IRManager {
public static String SAFE_COMPILER_PASSES = "";
public static String DEFAULT_COMPILER_PASSES = "OptimizeTempVarsPass,LocalOptimizationPass";
public static String DEFAULT_JIT_PASSES = "AddLocalVarLoadStoreInstructions,OptimizeDynScopesPass,AddCallProtocolInstructions,EnsureTempsAssigned";
public static String DEFAULT_JIT_PASSES = "DeadCodeElimination,AddLocalVarLoadStoreInstructions,OptimizeDynScopesPass,AddCallProtocolInstructions,EnsureTempsAssigned";
public static String DEFAULT_INLINING_COMPILER_PASSES = "LocalOptimizationPass";

private int dummyMetaClassCount = 0;
21 changes: 21 additions & 0 deletions core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java
Original file line number Diff line number Diff line change
@@ -1104,6 +1104,14 @@ public static void defCompiledClassMethod(ThreadContext context, MethodHandle ha
obj.callMethod(context, "singleton_method_added", context.runtime.fastNewSymbol(method.getName()));
}

@JIT
public static void defCompiledClassMethod(ThreadContext context, MethodHandle variable, MethodHandle specific, int specificArity, IRScope method, IRubyObject obj) {
RubyClass rubyClass = checkClassForDef(context, method, obj);

rubyClass.addMethod(method.getName(), new CompiledIRMethod(variable, specific, specificArity, method, Visibility.PUBLIC, rubyClass));
obj.callMethod(context, "singleton_method_added", context.runtime.fastNewSymbol(method.getName()));
}

private static RubyClass checkClassForDef(ThreadContext context, IRScope method, IRubyObject obj) {
if (obj instanceof RubyFixnum || obj instanceof RubySymbol) {
throw context.runtime.newTypeError("can't define singleton method \"" + method.getName() + "\" for " + obj.getMetaClass().getBaseName());
@@ -1140,6 +1148,19 @@ public static void defCompiledInstanceMethod(ThreadContext context, MethodHandle
Helpers.addInstanceMethod(clazz, method.getName(), newMethod, currVisibility, context, runtime);
}

@JIT
public static void defCompiledInstanceMethod(ThreadContext context, MethodHandle variable, MethodHandle specific, int specificArity, IRScope method, DynamicScope currDynScope, IRubyObject self) {
Ruby runtime = context.runtime;
RubyModule clazz = findInstanceMethodContainer(context, currDynScope, self);

Visibility currVisibility = context.getCurrentVisibility();
Visibility newVisibility = Helpers.performNormalMethodChecksAndDetermineVisibility(runtime, clazz, method.getName(), currVisibility);

DynamicMethod newMethod = new CompiledIRMethod(variable, specific, specificArity, method, newVisibility, clazz);

Helpers.addInstanceMethod(clazz, method.getName(), newMethod, currVisibility, context, runtime);
}

@JIT
public static IRubyObject invokeModuleBody(ThreadContext context, DynamicMethod method) {
RubyModule implClass = method.getImplementationClass();
20 changes: 15 additions & 5 deletions core/src/main/java/org/jruby/ir/targets/Bootstrap.java
Original file line number Diff line number Diff line change
@@ -824,18 +824,28 @@ private static MethodHandle getHandle(RubyClass selfClass, String fallbackName,
if (compiledIRMethod != null) {
assert compiledIRMethod.hasExplicitCallProtocol() : "all jitted methods must have call protocol";

mh = (MethodHandle)compiledIRMethod.getHandle();

binder = SmartBinder.from(site.signature)
.drop("caller");

// IR compiled methods only support varargs right now
if (site.arity == -1) {
// already [], nothing to do
mh = (MethodHandle)compiledIRMethod.getHandle();
} else if (site.arity == 0) {
binder = binder.insert(2, "args", IRubyObject.NULL_ARRAY);
MethodHandle specific;
if ((specific = compiledIRMethod.getHandleFor(site.arity)) != null) {
mh = specific;
} else {
mh = (MethodHandle)compiledIRMethod.getHandle();
binder = binder.insert(2, "args", IRubyObject.NULL_ARRAY);
}
} else {
binder = binder.collect("args", "arg.*");
MethodHandle specific;
if ((specific = compiledIRMethod.getHandleFor(site.arity)) != null) {
mh = specific;
} else {
mh = (MethodHandle) compiledIRMethod.getHandle();
binder = binder.collect("args", "arg.*");
}
}

if (!block) {
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/ir/targets/ClassData.java
Original file line number Diff line number Diff line change
@@ -72,7 +72,7 @@ private static final Type[] typesFromSignature(Signature signature) {
return types;
}

public abstract void pushmethod(String name, IRScope scope, Signature signature);
public abstract void pushmethod(String name, IRScope scope, Signature signature, boolean specificArity);

public void popmethod() {
method().endMethod();
5 changes: 3 additions & 2 deletions core/src/main/java/org/jruby/ir/targets/ClassData6.java
Original file line number Diff line number Diff line change
@@ -27,14 +27,15 @@ public ClassData6(String clsName, ClassVisitor cls) {
super(clsName, cls);
}

public void pushmethod(String name, IRScope scope, Signature signature) {
public void pushmethod(String name, IRScope scope, Signature signature, boolean specificArity) {
Method m = new Method(name, Type.getType(signature.type().returnType()), IRRuntimeHelpers.typesFromSignature(signature));
SkinnyMethodAdapter adapter = new SkinnyMethodAdapter(cls, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, m.getName(), m.getDescriptor(), null, null);
methodStack.push(
new MethodData(
new IRBytecodeAdapter6(adapter, signature, this),
scope,
signature)
signature,
specificArity ? scope.getStaticScope().getRequiredArgs() : -1)
);
}
}
5 changes: 3 additions & 2 deletions core/src/main/java/org/jruby/ir/targets/ClassData7.java
Original file line number Diff line number Diff line change
@@ -23,14 +23,15 @@ public ClassData7(String clsName, ClassVisitor cls) {
super(clsName, cls);
}

public void pushmethod(String name, IRScope scope, Signature signature) {
public void pushmethod(String name, IRScope scope, Signature signature, boolean specificArity) {
Method m = new Method(name, Type.getType(signature.type().returnType()), IRRuntimeHelpers.typesFromSignature(signature));
SkinnyMethodAdapter adapter = new SkinnyMethodAdapter(cls, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, m.getName(), m.getDescriptor(), null, null);
methodStack.push(
new MethodData(
new IRBytecodeAdapter7(adapter, signature, this),
scope,
signature)
signature,
specificArity ? scope.getStaticScope().getRequiredArgs() : -1)
);
}

4 changes: 2 additions & 2 deletions core/src/main/java/org/jruby/ir/targets/JVM.java
Original file line number Diff line number Diff line change
@@ -61,8 +61,8 @@ public IRBytecodeAdapter method() {
return clsData().method();
}

public void pushmethod(String name, IRScope scope, Signature signature) {
clsData().pushmethod(name, scope, signature);
public void pushmethod(String name, IRScope scope, Signature signature, boolean specificArity) {
clsData().pushmethod(name, scope, signature, specificArity);
method().startMethod();
}

182 changes: 137 additions & 45 deletions core/src/main/java/org/jruby/ir/targets/JVMVisitor.java
Original file line number Diff line number Diff line change
@@ -128,7 +128,7 @@ private void logScope(IRScope scope) {
LOG.info(b.toString());
}

public void emitScope(IRScope scope, String name, Signature signature) {
public void emitScope(IRScope scope, String name, Signature signature, boolean specificArity) {
List <BasicBlock> bbs = scope.prepareForCompilation();

Map <BasicBlock, Label> exceptionTable = scope.buildJVMExceptionTable();
@@ -137,17 +137,18 @@ public void emitScope(IRScope scope, String name, Signature signature) {

emitClosures(scope);

jvm.pushmethod(name, scope, signature);
jvm.pushmethod(name, scope, signature, specificArity);

// store IRScope in map for insertion into class later
scopeMap.put(name + "_IRScope", scope);
jvm.cls().visitField(Opcodes.ACC_STATIC | Opcodes.ACC_PUBLIC | Opcodes.ACC_VOLATILE, name + "_IRScope", ci(IRScope.class), null, null).visitEnd();

// UGLY hack for blocks and scripts, which still have their scopes pushed before invocation
// Scope management for blocks and scripts needs to be figured out
// FIXME: Seems like some methods are not triggering scope loading, so this is always done for now
if (true ||
scope instanceof IRClosure || scope instanceof IRScriptBody) {
String scopeField = name + "_IRScope";
if (scopeMap.get(scopeField) == null) {
scopeMap.put(scopeField, scope);
jvm.cls().visitField(Opcodes.ACC_STATIC | Opcodes.ACC_PUBLIC | Opcodes.ACC_VOLATILE, scopeField, ci(IRScope.class), null, null).visitEnd();
}

// Some scopes (closures, module/class bodies) do not have explicit call protocol yet.
// Unconditionally load current dynamic scope for those bodies.
if (!scope.hasExplicitCallProtocol()) {
jvmMethod().loadContext();
jvmMethod().invokeVirtual(Type.getType(ThreadContext.class), Method.getMethod("org.jruby.runtime.DynamicScope getCurrentScope()"));
jvmStoreLocal(DYNAMIC_SCOPE);
@@ -195,9 +196,32 @@ public void emitScope(IRScope scope, String name, Signature signature) {
jvm.popmethod();
}

private static final Signature METHOD_SIGNATURE = Signature
private static final Signature METHOD_SIGNATURE_BASE = Signature
.returning(IRubyObject.class)
.appendArgs(new String[]{"context", "scope", "self", "args", "block", "class"}, ThreadContext.class, StaticScope.class, IRubyObject.class, IRubyObject[].class, Block.class, RubyModule.class);
.appendArgs(new String[]{"context", "scope", "self", "block", "class"}, ThreadContext.class, StaticScope.class, IRubyObject.class, Block.class, RubyModule.class);

public static final Signature signatureFor(IRScope method, boolean aritySplit) {
if (aritySplit) {
StaticScope argScope = method.getStaticScope();
if (argScope.isArgumentScope() &&
argScope.getOptionalArgs() == 0 &&
argScope.getRestArg() == -1 &&
!method.receivesKeywordArgs()) {
// we have only required arguments...emit a signature appropriate to that arity
String[] args = new String[argScope.getRequiredArgs()];
Class[] types = Helpers.arrayOf(Class.class, args.length, IRubyObject.class);
for (int i = 0; i < args.length; i++) {
args[i] = "arg" + i;
}
return METHOD_SIGNATURE_BASE.insertArgs(3, args, types);
}
// we can't do an specific-arity signature
return null;
}

// normal boxed arg list signature
return METHOD_SIGNATURE_BASE.insertArgs(3, new String[]{"args"}, IRubyObject[].class);
}

private static final Signature CLOSURE_SIGNATURE = Signature
.returning(IRubyObject.class)
@@ -207,33 +231,51 @@ public void emitScriptBody(IRScriptBody script) {
String clsName = jvm.scriptToClass(script.getFileName());
jvm.pushscript(clsName, script.getFileName());

emitScope(script, "__script__", METHOD_SIGNATURE);
emitScope(script, "__script__", signatureFor(script, false), false);

jvm.cls().visitEnd();
jvm.popclass();
}

public Handle emitMethod(IRMethod method) {
public Map<Integer, Handle> emitMethod(IRMethod method) {
String name = JavaNameMangler.mangleMethodName(method.getName() + "_" + methodIndex++);

emitScope(method, name, METHOD_SIGNATURE);
Map<Integer, Handle> handles = new HashMap<>();

Signature signature = signatureFor(method, false);
emitScope(method, name, signature, false);
handles.put(-1, new Handle(Opcodes.H_INVOKESTATIC, jvm.clsData().clsName, name, sig(signature.type().returnType(), signature.type().parameterArray())));

return new Handle(Opcodes.H_INVOKESTATIC, jvm.clsData().clsName, name, sig(METHOD_SIGNATURE.type().returnType(), METHOD_SIGNATURE.type().parameterArray()));
Signature specificSig = signatureFor(method, true);
if (specificSig != null) {
emitScope(method, name, specificSig, true);
handles.put(method.getStaticScope().getRequiredArgs(), new Handle(Opcodes.H_INVOKESTATIC, jvm.clsData().clsName, name, sig(specificSig.type().returnType(), specificSig.type().parameterArray())));
}

return handles;
}

public Handle emitMethodJIT(IRMethod method) {
public Map<Integer, Handle> emitMethodJIT(IRMethod method) {
String name = JavaNameMangler.mangleMethodName(method.getName() + "_" + methodIndex++);
String clsName = jvm.scriptToClass(method.getFileName());
jvm.pushscript(clsName, method.getFileName());

emitScope(method, "__script__", METHOD_SIGNATURE);
Map<Integer, Handle> handles = new HashMap<>();

Signature signature = signatureFor(method, false);
emitScope(method, "__script__", signature, false);
handles.put(-1, new Handle(Opcodes.H_INVOKESTATIC, jvm.clsData().clsName, name, sig(signature.type().returnType(), signature.type().parameterArray())));

Handle handle = new Handle(Opcodes.H_INVOKESTATIC, jvm.clsData().clsName, name, sig(METHOD_SIGNATURE.type().returnType(), METHOD_SIGNATURE.type().parameterArray()));
Signature specificSig = signatureFor(method, true);
if (specificSig != null) {
emitScope(method, "__script__", specificSig, true);
handles.put(method.getStaticScope().getRequiredArgs(), new Handle(Opcodes.H_INVOKESTATIC, jvm.clsData().clsName, name, sig(specificSig.type().returnType(), specificSig.type().parameterArray())));
}

jvm.cls().visitEnd();
jvm.popclass();

return handle;
return handles;
}

public Handle emitModuleBodyJIT(IRModuleBody method) {
@@ -248,9 +290,10 @@ public Handle emitModuleBodyJIT(IRModuleBody method) {
String clsName = jvm.scriptToClass(method.getFileName());
jvm.pushscript(clsName, method.getFileName());

emitScope(method, "__script__", METHOD_SIGNATURE);
Signature signature = signatureFor(method, false);
emitScope(method, "__script__", signature, false);

Handle handle = new Handle(Opcodes.H_INVOKESTATIC, jvm.clsData().clsName, name, sig(METHOD_SIGNATURE.type().returnType(), METHOD_SIGNATURE.type().parameterArray()));
Handle handle = new Handle(Opcodes.H_INVOKESTATIC, jvm.clsData().clsName, name, sig(signature.type().returnType(), signature.type().parameterArray()));

jvm.cls().visitEnd();
jvm.popclass();
@@ -269,7 +312,7 @@ public Handle emitClosure(IRClosure closure) {
/* Compile the closure like a method */
String name = JavaNameMangler.mangleMethodName(closure.getName() + "__" + closure.getLexicalParent().getName() + "_" + methodIndex++);

emitScope(closure, name, CLOSURE_SIGNATURE);
emitScope(closure, name, CLOSURE_SIGNATURE, false);

return new Handle(Opcodes.H_INVOKESTATIC, jvm.clsData().clsName, name, sig(CLOSURE_SIGNATURE.type().returnType(), CLOSURE_SIGNATURE.type().parameterArray()));
}
@@ -284,9 +327,10 @@ public Handle emitModuleBody(IRModuleBody method) {
name = baseName + "_" + methodIndex++;
}

emitScope(method, name, METHOD_SIGNATURE);
Signature signature = signatureFor(method, false);
emitScope(method, name, signature, false);

return new Handle(Opcodes.H_INVOKESTATIC, jvm.clsData().clsName, name, sig(METHOD_SIGNATURE.type().returnType(), METHOD_SIGNATURE.type().parameterArray()));
return new Handle(Opcodes.H_INVOKESTATIC, jvm.clsData().clsName, name, sig(signature.type().returnType(), signature.type().parameterArray()));
}

public void visit(Instr instr) {
@@ -802,14 +846,18 @@ public void CheckArgsArrayArityInstr(CheckArgsArrayArityInstr checkargsarrayarit

@Override
public void CheckArityInstr(CheckArityInstr checkarityinstr) {
jvmMethod().loadContext();
jvmMethod().loadArgs();
jvmAdapter().ldc(checkarityinstr.required);
jvmAdapter().ldc(checkarityinstr.opt);
jvmAdapter().ldc(checkarityinstr.rest);
jvmAdapter().ldc(checkarityinstr.receivesKeywords);
jvmAdapter().ldc(checkarityinstr.restKey);
jvmAdapter().invokestatic(p(IRRuntimeHelpers.class), "checkArity", sig(void.class, ThreadContext.class, Object[].class, int.class, int.class, int.class, boolean.class, int.class));
if (jvm.methodData().specificArity >= 0) {
// no arity check in specific arity path
} else {
jvmMethod().loadContext();
jvmMethod().loadArgs();
jvmAdapter().ldc(checkarityinstr.required);
jvmAdapter().ldc(checkarityinstr.opt);
jvmAdapter().ldc(checkarityinstr.rest);
jvmAdapter().ldc(checkarityinstr.receivesKeywords);
jvmAdapter().ldc(checkarityinstr.restKey);
jvmAdapter().invokestatic(p(IRRuntimeHelpers.class), "checkArity", sig(void.class, ThreadContext.class, Object[].class, int.class, int.class, int.class, boolean.class, int.class));
}
}

@Override
@@ -894,14 +942,22 @@ public void DefineClassMethodInstr(DefineClassMethodInstr defineclassmethodinstr
IRMethod method = defineclassmethodinstr.getMethod();

jvmMethod().loadContext();
Handle handle = emitMethod(method);
jvmMethod().pushHandle(handle);

Map<Integer, Handle> handles = emitMethod(method);

Handle handle = handles.get(-1);

String defSignature = pushHandlesForDef(
handles,
handle,
sig(void.class, ThreadContext.class, java.lang.invoke.MethodHandle.class, IRScope.class, IRubyObject.class),
sig(void.class, ThreadContext.class, java.lang.invoke.MethodHandle.class, java.lang.invoke.MethodHandle.class, int.class, IRScope.class, IRubyObject.class));

jvmAdapter().getstatic(jvm.clsData().clsName, handle.getName() + "_IRScope", ci(IRScope.class));
visit(defineclassmethodinstr.getContainer());

// add method
jvmMethod().adapter.invokestatic(p(IRRuntimeHelpers.class), "defCompiledClassMethod",
sig(void.class, ThreadContext.class, java.lang.invoke.MethodHandle.class, IRScope.class, IRubyObject.class));
jvmMethod().adapter.invokestatic(p(IRRuntimeHelpers.class), "defCompiledClassMethod", defSignature);
}

// SSS FIXME: Needs an update to reflect instr. change
@@ -913,15 +969,45 @@ public void DefineInstanceMethodInstr(DefineInstanceMethodInstr defineinstanceme
SkinnyMethodAdapter a = m.adapter;

m.loadContext();
Handle handle = emitMethod(method);
jvmMethod().pushHandle(handle);

Map<Integer, Handle> handles = emitMethod(method);

Handle handle = handles.get(-1); // always a variable arity handle

String defSignature = pushHandlesForDef(
handles,
handle,
sig(void.class, ThreadContext.class, java.lang.invoke.MethodHandle.class, IRScope.class, DynamicScope.class, IRubyObject.class),
sig(void.class, ThreadContext.class, java.lang.invoke.MethodHandle.class, java.lang.invoke.MethodHandle.class, int.class, IRScope.class, DynamicScope.class, IRubyObject.class));

a.getstatic(jvm.clsData().clsName, handle.getName() + "_IRScope", ci(IRScope.class));
jvmLoadLocal(DYNAMIC_SCOPE);
jvmMethod().loadSelf();

// add method
a.invokestatic(p(IRRuntimeHelpers.class), "defCompiledInstanceMethod",
sig(void.class, ThreadContext.class, java.lang.invoke.MethodHandle.class, IRScope.class, DynamicScope.class, IRubyObject.class));
a.invokestatic(p(IRRuntimeHelpers.class), "defCompiledInstanceMethod", defSignature);
}

public String pushHandlesForDef(Map<Integer, Handle> handles, Handle variable, String variableOnly, String variableAndSpecific) {
String defSignature;

if (handles.size() == 1) {
defSignature = variableOnly;
jvmMethod().pushHandle(variable);
} else {
defSignature = variableAndSpecific;

jvmMethod().pushHandle(variable);

// FIXME: only supports one specific arity
for (Map.Entry<Integer, Handle> entry : handles.entrySet()) {
if (entry.getKey() == -1) continue; // variable arity handle pushed above
jvmMethod().pushHandle(entry.getValue());
jvmAdapter().pushInt(entry.getKey());
break;
}
}
return defSignature;
}

@Override
@@ -1362,10 +1448,16 @@ public void ReceiveOptArgInstr(ReceiveOptArgInstr instr) {

@Override
public void ReceivePreReqdArgInstr(ReceivePreReqdArgInstr instr) {
jvmMethod().loadContext();
jvmMethod().loadArgs();
jvmAdapter().pushInt(instr.getArgIndex());
jvmMethod().invokeIRHelper("getPreArgSafe", sig(IRubyObject.class, ThreadContext.class, IRubyObject[].class, int.class));
if (jvm.methodData().specificArity >= 0 &&
instr.getArgIndex() < jvm.methodData().specificArity) {
System.out.println(jvm.methodData().signature);
jvmAdapter().aload(jvm.methodData().signature.argOffset("arg" + instr.getArgIndex()));
} else {
jvmMethod().loadContext();
jvmMethod().loadArgs();
jvmAdapter().pushInt(instr.getArgIndex());
jvmMethod().invokeIRHelper("getPreArgSafe", sig(IRubyObject.class, ThreadContext.class, IRubyObject[].class, int.class));
}
jvmStoreLocal(instr.getResult());
}

6 changes: 5 additions & 1 deletion core/src/main/java/org/jruby/ir/targets/MethodData.java
Original file line number Diff line number Diff line change
@@ -20,9 +20,11 @@
*/
public class MethodData {

public MethodData(IRBytecodeAdapter method, IRScope scope, Signature signature) {
public MethodData(IRBytecodeAdapter method, IRScope scope, Signature signature, int specificArity) {
this.method = method;
this.scope = scope;
this.signature = signature;
this.specificArity = specificArity;

// incoming arguments
for (int i = 0; i < signature.argCount(); i++) {
@@ -66,6 +68,8 @@ public org.objectweb.asm.Label getLabel(Label label) {

public final IRBytecodeAdapter method;
public final IRScope scope;
public final Signature signature;
public final int specificArity;
public final Map<String, Integer> varMap = new HashMap<String, Integer>();
public final Map<Label, org.objectweb.asm.Label> labelMap = new HashMap<Label, org.objectweb.asm.Label>();

7 changes: 7 additions & 0 deletions core/src/main/java/org/jruby/runtime/Helpers.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.jruby.runtime;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.nio.channels.ClosedChannelException;
import java.nio.charset.Charset;

@@ -3118,6 +3119,12 @@ public static <T> T[] arrayOf(T... values) {
return values;
}

public static <T> T[] arrayOf(Class<T> t, int size, T fill) {
T[] ary = (T[])Array.newInstance(t, size);
Arrays.fill(ary, fill);
return ary;
}

@Deprecated
public static StaticScope decodeRootScope(ThreadContext context, String scopeString) {
return decodeScope(context, null, scopeString);

0 comments on commit 31f7926

Please sign in to comment.