Skip to content

Commit

Permalink
Restructure JIT to happen in one place each for methods and blocks
Browse files Browse the repository at this point in the history
This introduces a third method and block top, the "OptInterpreter"
version. All methods and blocks in a normal run are MixedMode now,
aggregating both an Interpreted and a slot for an optimized or
jitted version. This commit also simplifies some call paths in
the IR block bodies and lowers several methods into
CompiledIRBlockBody to be more direct.
headius committed Mar 22, 2018
1 parent 6902809 commit b1b1644
Showing 19 changed files with 491 additions and 636 deletions.
7 changes: 3 additions & 4 deletions core/src/main/java/org/jruby/RubyModule.java
Original file line number Diff line number Diff line change
@@ -92,7 +92,7 @@
import org.jruby.runtime.CallSite;
import org.jruby.runtime.ClassIndex;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.IRBlockBody;
import org.jruby.runtime.AbstractIRBlockBody;
import org.jruby.runtime.MethodFactory;
import org.jruby.runtime.MethodIndex;
import org.jruby.runtime.ObjectAllocator;
@@ -105,7 +105,6 @@
import org.jruby.runtime.ivars.MethodData;
import org.jruby.runtime.marshal.MarshalStream;
import org.jruby.runtime.marshal.UnmarshalStream;
import org.jruby.runtime.opto.ConstantInvalidator;
import org.jruby.runtime.opto.Invalidator;
import org.jruby.runtime.opto.OptoFactory;
import org.jruby.runtime.profile.MethodEnhancer;
@@ -1991,9 +1990,9 @@ public IRubyObject defineMethodFromBlock(ThreadContext context, IRubyObject arg0

// If we know it comes from IR we can convert this directly to a method and
// avoid overhead of invoking it as a block
if (block.getBody() instanceof IRBlockBody &&
if (block.getBody() instanceof AbstractIRBlockBody &&
runtime.getInstanceConfig().getCompileMode().shouldJIT()) { // FIXME: Once Interp and Mixed Methods are one class we can fix this to work in interp mode too.
IRBlockBody body = (IRBlockBody) block.getBody();
AbstractIRBlockBody body = (AbstractIRBlockBody) block.getBody();
IRClosure closure = body.getScope();

// Ask closure to give us a method equivalent.
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
package org.jruby.compiler;

import org.jruby.ir.interpreter.InterpreterContext;
import org.jruby.runtime.MixedModeIRBlockBody;
import org.jruby.runtime.OptInterpretedIRBlockBody;

/**
* Created by headius on 12/8/16.
*/
class FullBuildTask implements Runnable {
class BlockFullBuildTask implements Runnable {
private JITCompiler jitCompiler;
private final Compilable<InterpreterContext> method;
private final MixedModeIRBlockBody method;

FullBuildTask(JITCompiler jitCompiler, Compilable<InterpreterContext> method) {
BlockFullBuildTask(JITCompiler jitCompiler, MixedModeIRBlockBody method) {
this.jitCompiler = jitCompiler;
this.method = method;
}

public void run() {
try {
method.getIRScope().getRootLexicalScope().prepareFullBuild();

method.completeBuild(method.getIRScope().prepareFullBuild());
method.completeBuild(new OptInterpretedIRBlockBody(method.getScope(), method.getSignature()));

if (jitCompiler.config.isJitLogging()) {
JITCompiler.log(method.getImplementationClass(), method.getFile(), method.getLine(), method.getName(), "done building");
16 changes: 13 additions & 3 deletions core/src/main/java/org/jruby/compiler/JITCompiler.java
Original file line number Diff line number Diff line change
@@ -146,13 +146,23 @@ public void tearDown() {
}

public Runnable getTaskFor(ThreadContext context, Compilable method) {
boolean shouldJit = context.runtime.getInstanceConfig().getCompileMode().shouldJIT();

if (method instanceof MixedModeIRMethod) {
return new MethodJITTask(this, (MixedModeIRMethod) method, method.getClassName(context));
if (shouldJit) {
return new MethodJITTask(this, (MixedModeIRMethod) method, method.getClassName(context));
} else {
return new MethodFullBuildTask(this, (MixedModeIRMethod) method);
}
} else if (method instanceof MixedModeIRBlockBody) {
return new BlockJITTask(this, (MixedModeIRBlockBody) method, method.getClassName(context));
if (shouldJit) {
return new BlockJITTask(this, (MixedModeIRBlockBody) method, method.getClassName(context));
} else {
return new BlockFullBuildTask(this, (MixedModeIRBlockBody) method);
}
}

return new FullBuildTask(this, method);
throw new RuntimeException("unknown method type for JIT: " + method);
}

public void buildThresholdReached(ThreadContext context, final Compilable method) {
34 changes: 34 additions & 0 deletions core/src/main/java/org/jruby/compiler/MethodFullBuildTask.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.jruby.compiler;

import org.jruby.internal.runtime.methods.MixedModeIRMethod;
import org.jruby.internal.runtime.methods.OptInterpretedIRMethod;
import org.jruby.runtime.MixedModeIRBlockBody;
import org.jruby.runtime.OptInterpretedIRBlockBody;

class MethodFullBuildTask implements Runnable {
private JITCompiler jitCompiler;
private final MixedModeIRMethod method;

MethodFullBuildTask(JITCompiler jitCompiler, MixedModeIRMethod method) {
this.jitCompiler = jitCompiler;
this.method = method;
}

public void run() {
try {
method.completeBuild(new OptInterpretedIRMethod(method.getIRScope(), method.getVisibility(), method.getImplementationClass()));

if (jitCompiler.config.isJitLogging()) {
JITCompiler.log(method.getImplementationClass(), method.getFile(), method.getLine(), method.getName(), "done building");
}
} catch (Throwable t) {
if (jitCompiler.config.isJitLogging()) {
JITCompiler.log(method.getImplementationClass(), method.getFile(), method.getLine(), method.getName(),
"Could not build; passes run: " + method.getIRScope().getExecutedPasses(), t.getMessage());
if (jitCompiler.config.isJitLoggingVerbose()) {
t.printStackTrace();
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -28,8 +28,6 @@ public abstract class AbstractIRMethod extends DynamicMethod implements IRMethod
protected final Signature signature;
protected final IRScope method;
protected final StaticScope staticScope;
protected InterpreterContext interpreterContext = null;
protected int callCount = 0;
private MethodData methodData;

public AbstractIRMethod(IRScope method, Visibility visibility, RubyModule implementationClass) {
@@ -38,24 +36,12 @@ public AbstractIRMethod(IRScope method, Visibility visibility, RubyModule implem
this.staticScope = method.getStaticScope();
this.staticScope.determineModule();
this.signature = staticScope.getSignature();

// -1 jit.threshold is way of having interpreter not promote full builds.
if (Options.JIT_THRESHOLD.load() == -1) callCount = -1;

// If we are printing, do the build right at creation time so we can see it
if (IRRuntimeHelpers.shouldPrintIR(implementationClass.getRuntime())) {
ensureInstrsReady();
}
}

public IRScope getIRScope() {
return method;
}

public void setCallCount(int callCount) {
this.callCount = callCount;
}

public StaticScope getStaticScope() {
return staticScope;
}
Original file line number Diff line number Diff line change
@@ -3,7 +3,6 @@
import org.jruby.RubyModule;
import org.jruby.ir.IRScope;
import org.jruby.ir.interpreter.InterpreterContext;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.runtime.ArgumentDescriptor;
import org.jruby.runtime.Block;
import org.jruby.runtime.ThreadContext;
@@ -16,8 +15,6 @@
public class InterpretedIRBodyMethod extends InterpretedIRMethod {
public InterpretedIRBodyMethod(IRScope method, RubyModule implementationClass) {
super(method, Visibility.PUBLIC, implementationClass);

callCount = -1;
}

@Override
@@ -27,22 +24,58 @@ public ArgumentDescriptor[] getArgumentDescriptors() {

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
return call(context, self, clazz, name, block);
return callInternal(context, self, clazz, name, block);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
if (IRRuntimeHelpers.isDebug()) doDebug();
return callInternal(context, self, clazz, name, block);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, Block block) {
return callInternal(context, self, clazz, name, block);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, Block block) {
return callInternal(context, self, clazz, name, block);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
return callInternal(context, self, clazz, name, block);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args) {
return callInternal(context, self, clazz, name, Block.NULL_BLOCK);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name) {
return callInternal(context, self, clazz, name, Block.NULL_BLOCK);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0) {
return callInternal(context, self, clazz, name, Block.NULL_BLOCK);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1) {
return callInternal(context, self, clazz, name, Block.NULL_BLOCK);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
return callInternal(context, self, clazz, name, Block.NULL_BLOCK);
}

protected IRubyObject callInternal(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
InterpreterContext ic = ensureInstrsReady();

boolean hasExplicitCallProtocol = ic.hasExplicitCallProtocol();

if (!hasExplicitCallProtocol) this.pre(ic, context, self, name, block, getImplementationClass());
this.pre(ic, context, self, name, block, getImplementationClass());

try {
switch (method.getScopeType()) {
@@ -52,7 +85,7 @@ protected IRubyObject callInternal(ThreadContext context, IRubyObject self, Ruby
default: throw new RuntimeException("invalid body method type: " + method);
}
} finally {
if (!hasExplicitCallProtocol) this.post(ic, context);
this.post(ic, context);
}
}

@@ -76,44 +109,4 @@ private IRubyObject interpretWithBacktrace(InterpreterContext ic, ThreadContext
ThreadContext.popBacktrace(context);
}
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, Block block) {
return call(context, self, clazz, name, block);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, Block block) {
return call(context, self, clazz, name, block);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
return call(context, self, clazz, name, block);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args) {
return call(context, self, clazz, name, Block.NULL_BLOCK);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name) {
return call(context, self, clazz, name, Block.NULL_BLOCK);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0) {
return call(context, self, clazz, name, Block.NULL_BLOCK);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1) {
return call(context, self, clazz, name, Block.NULL_BLOCK);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
return call(context, self, clazz, name, Block.NULL_BLOCK);
}
}
Original file line number Diff line number Diff line change
@@ -35,11 +35,4 @@ protected void pre(InterpreterContext ic, ThreadContext context, IRubyObject sel
}
context.setCurrentVisibility(getVisibility());
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
if (IRRuntimeHelpers.isDebug()) doDebug();

return callInternal(context, self, clazz, name, block);
}
}
Original file line number Diff line number Diff line change
@@ -23,30 +23,20 @@
/**
* Method for -X-C (interpreted only execution). See MixedModeIRMethod for inter/JIT method impl.
*/
public class InterpretedIRMethod extends AbstractIRMethod implements Compilable<InterpreterContext> {
public class InterpretedIRMethod extends AbstractIRMethod {
private static final Logger LOG = LoggerFactory.getLogger(InterpretedIRMethod.class);

private boolean displayedCFG = false; // FIXME: Remove when we find nicer way of logging CFG

protected InterpreterContext interpreterContext = null;
protected int callCount = 0;
protected InterpreterContext interpreterContext;

public InterpretedIRMethod(IRScope method, Visibility visibility, RubyModule implementationClass) {
super(method, visibility, implementationClass);

// -1 jit.threshold is way of having interpreter not promote full builds.
if (Options.JIT_THRESHOLD.load() == -1) callCount = -1;

// If we are printing, do the build right at creation time so we can see it
if (IRRuntimeHelpers.shouldPrintIR(implementationClass.getRuntime())) {
ensureInstrsReady();
interpreterContext = ensureInstrsReady();
}
}

public void setCallCount(int callCount) {
this.callCount = callCount;
}

protected void post(InterpreterContext ic, ThreadContext context) {
// update call stacks (pop: ..)
context.popFrame();
@@ -73,6 +63,11 @@ public InterpreterContext ensureInstrsReady() {
}
interpreterContext = method.getInterpreterContext();

if (IRRuntimeHelpers.isDebug()) {
LOG.info("Executing '" + method.getName() + "'");
LOG.info(method.debugOutput());
}

if (IRRuntimeHelpers.shouldPrintIR(implementationClass.getRuntime())) {
ByteArrayOutputStream baos = IRDumper.printIR(method, false, true);

@@ -85,224 +80,111 @@ public InterpreterContext ensureInstrsReady() {

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
if (IRRuntimeHelpers.isDebug()) doDebug();

if (callCount >= 0) promoteToFullBuild(context);
return INTERPRET_METHOD(context, ensureInstrsReady(), getImplementationClass(), self, name, args, block);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args) {
if (IRRuntimeHelpers.isDebug()) doDebug();

if (callCount >= 0) promoteToFullBuild(context);
return INTERPRET_METHOD(context, ensureInstrsReady(), getImplementationClass(), self, name, args, Block.NULL_BLOCK);
}

private IRubyObject INTERPRET_METHOD(ThreadContext context, InterpreterContext ic, RubyModule implClass,
IRubyObject self, String name, IRubyObject[] args, Block block) {
try {
ThreadContext.pushBacktrace(context, name, ic.getFileName(), context.getLine());

if (ic.hasExplicitCallProtocol()) {
return ic.getEngine().interpret(context, null, self, ic, implClass, name, args, block);
} else {
try {
pre(ic, context, self, name, block, implClass);
return ic.getEngine().interpret(context, null, self, ic, implClass, name, args, block);
} finally {
post(ic, context);
}
}
pre(ic, context, self, name, block, implClass);
return ic.getEngine().interpret(context, null, self, ic, implClass, name, args, block);
} finally {
post(ic, context);
ThreadContext.popBacktrace(context);
}
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
if (IRRuntimeHelpers.isDebug()) doDebug();

if (callCount >= 0) promoteToFullBuild(context);

return INTERPRET_METHOD(context, ensureInstrsReady(), getImplementationClass(), self, name, block);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name) {
if (IRRuntimeHelpers.isDebug()) doDebug();

if (callCount >= 0) promoteToFullBuild(context);

return INTERPRET_METHOD(context, ensureInstrsReady(), getImplementationClass(), self, name, Block.NULL_BLOCK);
}

private IRubyObject INTERPRET_METHOD(ThreadContext context, InterpreterContext ic, RubyModule implClass,
IRubyObject self, String name, Block block) {
try {
ThreadContext.pushBacktrace(context, name, ic.getFileName(), context.getLine());

if (ic.hasExplicitCallProtocol()) {
return ic.getEngine().interpret(context, null, self, ic, implClass, name, block);
} else {
try {
pre(ic, context, self, name, block, implClass);
return ic.getEngine().interpret(context, null, self, ic, implClass, name, block);
} finally {
post(ic, context);
}
}
pre(ic, context, self, name, block, implClass);
return ic.getEngine().interpret(context, null, self, ic, implClass, name, block);
} finally {
post(ic, context);
ThreadContext.popBacktrace(context);
}
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, Block block) {
if (IRRuntimeHelpers.isDebug()) doDebug();

if (callCount >= 0) promoteToFullBuild(context);
return INTERPRET_METHOD(context, ensureInstrsReady(), getImplementationClass(), self, name, arg0, block);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0) {
if (IRRuntimeHelpers.isDebug()) doDebug();

if (callCount >= 0) promoteToFullBuild(context);
return INTERPRET_METHOD(context, ensureInstrsReady(), getImplementationClass(), self, name, arg0, Block.NULL_BLOCK);
}

private IRubyObject INTERPRET_METHOD(ThreadContext context, InterpreterContext ic, RubyModule implClass,
IRubyObject self, String name, IRubyObject arg1, Block block) {
try {
ThreadContext.pushBacktrace(context, name, ic.getFileName(), context.getLine());

if (ic.hasExplicitCallProtocol()) {
return ic.getEngine().interpret(context, null, self, ic, implClass, name, arg1, block);
} else {
try {
pre(ic, context, self, name, block, implClass);
return ic.getEngine().interpret(context, null, self, ic, implClass, name, arg1, block);
} finally {
post(ic, context);
}
}
pre(ic, context, self, name, block, implClass);
return ic.getEngine().interpret(context, null, self, ic, implClass, name, arg1, block);
} finally {
post(ic, context);
ThreadContext.popBacktrace(context);
}
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, Block block) {
if (IRRuntimeHelpers.isDebug()) doDebug();

if (callCount >= 0) promoteToFullBuild(context);
return INTERPRET_METHOD(context, ensureInstrsReady(), getImplementationClass(), self, name, arg0, arg1, block);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1) {
if (IRRuntimeHelpers.isDebug()) doDebug();

if (callCount >= 0) promoteToFullBuild(context);
return INTERPRET_METHOD(context, ensureInstrsReady(), getImplementationClass(), self, name, arg0, arg1, Block.NULL_BLOCK);
}

private IRubyObject INTERPRET_METHOD(ThreadContext context, InterpreterContext ic, RubyModule implClass,
IRubyObject self, String name, IRubyObject arg1, IRubyObject arg2, Block block) {
IRubyObject self, String name, IRubyObject arg1, IRubyObject arg2, Block block) {
try {
ThreadContext.pushBacktrace(context, name, ic.getFileName(), context.getLine());

if (ic.hasExplicitCallProtocol()) {
return ic.getEngine().interpret(context, null, self, ic, implClass, name, arg1, arg2, block);
} else {
try {
pre(ic, context, self, name, block, implClass);
return ic.getEngine().interpret(context, null, self, ic, implClass, name, arg1, arg2, block);
} finally {
post(ic, context);
}
}
pre(ic, context, self, name, block, implClass);
return ic.getEngine().interpret(context, null, self, ic, implClass, name, arg1, arg2, block);
} finally {
post(ic, context);
ThreadContext.popBacktrace(context);
}
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
if (IRRuntimeHelpers.isDebug()) doDebug();

if (callCount >= 0) promoteToFullBuild(context);
return INTERPRET_METHOD(context, ensureInstrsReady(), getImplementationClass(), self, name, arg0, arg1, arg2, block);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
if (IRRuntimeHelpers.isDebug()) doDebug();

if (callCount >= 0) promoteToFullBuild(context);
return INTERPRET_METHOD(context, ensureInstrsReady(), getImplementationClass(), self, name, arg0, arg1, arg2, Block.NULL_BLOCK);
}

private IRubyObject INTERPRET_METHOD(ThreadContext context, InterpreterContext ic, RubyModule implClass,
IRubyObject self, String name, IRubyObject arg1, IRubyObject arg2, IRubyObject arg3, Block block) {
try {
ThreadContext.pushBacktrace(context, name, ic.getFileName(), context.getLine());

if (ic.hasExplicitCallProtocol()) {
return ic.getEngine().interpret(context, null, self, ic, implClass, name, arg1, arg2, arg3, block);
} else {
try {
pre(ic, context, self, name, block, implClass);
return ic.getEngine().interpret(context, null, self, ic, implClass, name, arg1, arg2, arg3, block);
} finally {
post(ic, context);
}
}
pre(ic, context, self, name, block, implClass);
return ic.getEngine().interpret(context, null, self, ic, implClass, name, arg1, arg2, arg3, block);
} finally {
post(ic, context);
ThreadContext.popBacktrace(context);
}

}

protected void doDebug() {
// FIXME: This is printing out IRScope CFG but JIT may be active and it might not reflect
// currently executing. Move into JIT and into interp since they will be getting CFG from
// different sources
// FIXME: This is only printing out CFG once. If we keep applying more passes then we
// will want to print out after those new passes.
ensureInstrsReady();
LOG.info("Executing '" + method.getName() + "'");
if (!displayedCFG) {
LOG.info(method.debugOutput());
displayedCFG = true;
}
}

public void completeBuild(InterpreterContext interpreterContext) {
this.interpreterContext = interpreterContext;
// Reset so that we can see the new instr dump again
this.displayedCFG = false;
}

// Unlike JIT in MixedMode this will always successfully build but if using executor pool it may take a while
// and replace interpreterContext asynchronously.
private void promoteToFullBuild(ThreadContext context) {
Ruby runtime = context.runtime;

if (runtime.isBooting() && !Options.JIT_KERNEL.load()) return; // don't Promote to full build during runtime boot

if (callCount++ >= Options.JIT_THRESHOLD.load()) runtime.getJITCompiler().buildThresholdReached(context, this);

if (IRRuntimeHelpers.shouldPrintIR(implementationClass.getRuntime())) {
ByteArrayOutputStream baos = IRDumper.printIR(method, true, true);

LOG.info("Printing full IR for " + method.getName() + ":\n" + new String(baos.toByteArray()));
}
}

public String getClassName(ThreadContext context) {
return null;
}
}
Original file line number Diff line number Diff line change
@@ -26,241 +26,72 @@ public class MixedModeIRMethod extends AbstractIRMethod implements Compilable<Dy
private boolean displayedCFG = false; // FIXME: Remove when we find nicer way of logging CFG

private volatile int callCount = 0;
private volatile DynamicMethod actualMethod;
private volatile InterpretedIRMethod baseMethod;
private volatile DynamicMethod jittedMethod;

public MixedModeIRMethod(IRScope method, Visibility visibility, RubyModule implementationClass) {
super(method, visibility, implementationClass);

this.baseMethod = new InterpretedIRMethod(method, visibility, implementationClass);

getStaticScope().determineModule();

// disable JIT if JIT is disabled
if (!implementationClass.getRuntime().getInstanceConfig().getCompileMode().shouldJIT() ||
Options.JIT_THRESHOLD.load() < 0) {
// disable JIT if threshold is below zero
if (Options.JIT_THRESHOLD.load() < 0) {
callCount = -1;
}
}

public DynamicMethod getActualMethod() {
return actualMethod;
}

protected void post(InterpreterContext ic, ThreadContext context) {
// update call stacks (pop: ..)
context.popFrame();
if (ic.popDynScope()) {
context.popScope();
}
}

protected void pre(InterpreterContext ic, ThreadContext context, IRubyObject self, String name, Block block, RubyModule implClass) {
// update call stacks (push: frame, class, scope, etc.)
context.preMethodFrameOnly(implClass, name, self, block);
if (ic.pushNewDynScope()) {
context.pushScope(DynamicScope.newDynamicScope(ic.getStaticScope()));
}
return jittedMethod != null ? jittedMethod : baseMethod;
}

// FIXME: for subclasses we should override this method since it can be simple get
// FIXME: to avoid cost of synch call in lazilyacquire we can save the ic here
public InterpreterContext ensureInstrsReady() {
if (method instanceof IRMethod) {
return ((IRMethod) method).lazilyAcquireInterpreterContext();
}

InterpreterContext ic = method.getInterpreterContext();

if (IRRuntimeHelpers.shouldPrintIR(implementationClass.getRuntime())) {
ByteArrayOutputStream baos = IRDumper.printIR(method, false);

LOG.info("Printing simple IR for " + method.getName() + ":\n" + new String(baos.toByteArray()));
}

return ic;
return baseMethod.ensureInstrsReady();
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
if (IRRuntimeHelpers.isDebug()) doDebug();

if (callCount >= 0) tryJit(context);

DynamicMethod jittedMethod = actualMethod;
if (jittedMethod != null) {
return jittedMethod.call(context, self, clazz, name, args, block);
}
return INTERPRET_METHOD(context, ensureInstrsReady(), getImplementationClass().getMethodLocation(), self, name, args, block);
}

private IRubyObject INTERPRET_METHOD(ThreadContext context, InterpreterContext ic, RubyModule implClass,
IRubyObject self, String name, IRubyObject[] args, Block block) {
try {
ThreadContext.pushBacktrace(context, name, ic.getFileName(), context.getLine());

if (ic.hasExplicitCallProtocol()) {
return ic.getEngine().interpret(context, null, self, ic, implClass, name, args, block);
} else {
try {
this.pre(ic, context, self, name, block, implClass);
return ic.getEngine().interpret(context, null, self, ic, implClass, name, args, block);
} finally {
this.post(ic, context);
}
}
} finally {
ThreadContext.popBacktrace(context);
}
return getActualMethod().call(context, self, clazz, name, args, block);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
if (IRRuntimeHelpers.isDebug()) doDebug();

if (callCount >= 0) tryJit(context);

DynamicMethod jittedMethod = actualMethod;
if (jittedMethod != null) {
return jittedMethod.call(context, self, clazz, name, block);
}
return INTERPRET_METHOD(context, ensureInstrsReady(), getImplementationClass().getMethodLocation(), self, name, block);
}

private IRubyObject INTERPRET_METHOD(ThreadContext context, InterpreterContext ic, RubyModule implClass,
IRubyObject self, String name, Block block) {
try {
ThreadContext.pushBacktrace(context, name, ic.getFileName(), context.getLine());

if (ic.hasExplicitCallProtocol()) {
return ic.getEngine().interpret(context, null, self, ic, implClass, name, block);
} else {
try {
this.pre(ic, context, self, name, block, implClass);
return ic.getEngine().interpret(context, null, self, ic, implClass, name, block);
} finally {
this.post(ic, context);
}
}
} finally {
ThreadContext.popBacktrace(context);
}
return getActualMethod().call(context, self, clazz, name, block);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, Block block) {
if (IRRuntimeHelpers.isDebug()) doDebug();

if (callCount >= 0) tryJit(context);

DynamicMethod jittedMethod = actualMethod;
if (jittedMethod != null) {
return jittedMethod.call(context, self, clazz, name, arg0, block);
}
return INTERPRET_METHOD(context, ensureInstrsReady(), getImplementationClass().getMethodLocation(), self, name, arg0, block);
}

private IRubyObject INTERPRET_METHOD(ThreadContext context, InterpreterContext ic, RubyModule implClass,
IRubyObject self, String name, IRubyObject arg1, Block block) {
try {
ThreadContext.pushBacktrace(context, name, ic.getFileName(), context.getLine());

if (ic.hasExplicitCallProtocol()) {
return ic.getEngine().interpret(context, null, self, ic, implClass, name, arg1, block);
} else {
try {
this.pre(ic, context, self, name, block, implClass);
return ic.getEngine().interpret(context, null, self, ic, implClass, name, arg1, block);
} finally {
this.post(ic, context);
}
}
} finally {
ThreadContext.popBacktrace(context);
}
return getActualMethod().call(context, self, clazz, name, arg0, block);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, Block block) {
if (IRRuntimeHelpers.isDebug()) doDebug();

if (callCount >= 0) tryJit(context);

DynamicMethod jittedMethod = actualMethod;
if (jittedMethod != null) {
return jittedMethod.call(context, self, clazz, name, arg0, arg1, block);
}
return INTERPRET_METHOD(context, ensureInstrsReady(), getImplementationClass().getMethodLocation(), self, name, arg0, arg1, block);
}

private IRubyObject INTERPRET_METHOD(ThreadContext context, InterpreterContext ic, RubyModule implClass,
IRubyObject self, String name, IRubyObject arg1, IRubyObject arg2, Block block) {
try {
ThreadContext.pushBacktrace(context, name, ic.getFileName(), context.getLine());

if (ic.hasExplicitCallProtocol()) {
return ic.getEngine().interpret(context, null, self, ic, implClass, name, arg1, arg2, block);
} else {
try {
this.pre(ic, context, self, name, block, implClass);
return ic.getEngine().interpret(context, null, self, ic, implClass, name, arg1, arg2, block);
} finally {
this.post(ic, context);
}
}
} finally {
ThreadContext.popBacktrace(context);
}
return getActualMethod().call(context, self, clazz, name, arg0, arg1, block);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
if (IRRuntimeHelpers.isDebug()) doDebug();
if (callCount >= 0) tryJit(context);

DynamicMethod jittedMethod = actualMethod;
if (jittedMethod != null) {
return jittedMethod.call(context, self, clazz, name, arg0, arg1, arg2, block);
}
return INTERPRET_METHOD(context, ensureInstrsReady(), getImplementationClass().getMethodLocation(), self, name, arg0, arg1, arg2, block);
}

private IRubyObject INTERPRET_METHOD(ThreadContext context, InterpreterContext ic, RubyModule implClass,
IRubyObject self, String name, IRubyObject arg1, IRubyObject arg2, IRubyObject arg3, Block block) {
try {
ThreadContext.pushBacktrace(context, name, ic.getFileName(), context.getLine());

if (ic.hasExplicitCallProtocol()) {
return ic.getEngine().interpret(context, null, self, ic, implClass, name, arg1, arg2, arg3, block);
} else {
try {
this.pre(ic, context, self, name, block, implClass);
return ic.getEngine().interpret(context, null, self, ic, implClass, name, arg1, arg2, arg3, block);
} finally {
this.post(ic, context);
}
}
} finally {
ThreadContext.popBacktrace(context);
}

}

private void doDebug() {
// FIXME: This is printing out IRScope CFG but JIT may be active and it might not reflect
// currently executing. Move into JIT and into interp since they will be getting CFG from
// different sources
// FIXME: This is only printing out CFG once. If we keep applying more passes then we
// will want to print out after those new passes.
ensureInstrsReady();
LOG.info("Executing '" + method.getName() + "'");
if (!displayedCFG) {
LOG.info(method.debugOutput());
displayedCFG = true;
}
return getActualMethod().call(context, self, clazz, name, arg0, arg1, arg2, block);
}

@Override
public void completeBuild(DynamicMethod newMethod) {
setCallCount(-1);
newMethod.serialNumber = this.serialNumber;
actualMethod = newMethod;
jittedMethod = newMethod;
getImplementationClass().invalidateCacheDescendants();
}

@@ -298,7 +129,7 @@ public String getClassName(ThreadContext context) {
public DynamicMethod dup() {
MixedModeIRMethod x = (MixedModeIRMethod) super.dup();
x.callCount = callCount;
x.actualMethod = actualMethod;
x.baseMethod = baseMethod;

return x;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package org.jruby.internal.runtime.methods;

import org.jruby.RubyModule;
import org.jruby.internal.runtime.AbstractIRMethod;
import org.jruby.ir.IRMethod;
import org.jruby.ir.IRScope;
import org.jruby.ir.interpreter.FullInterpreterContext;
import org.jruby.ir.interpreter.InterpreterContext;
import org.jruby.ir.persistence.IRDumper;
import org.jruby.ir.runtime.IRRuntimeHelpers;
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 org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;

import java.io.ByteArrayOutputStream;

/**
* Method for -X-C (interpreted only execution). See MixedModeIRMethod for inter/JIT method impl.
*/
public class OptInterpretedIRMethod extends AbstractIRMethod {
private static final Logger LOG = LoggerFactory.getLogger(OptInterpretedIRMethod.class);

protected FullInterpreterContext interpreterContext;

public OptInterpretedIRMethod(IRScope method, Visibility visibility, RubyModule implementationClass) {
super(method, visibility, implementationClass);

interpreterContext = method.prepareFullBuild();
}

@Override
public InterpreterContext ensureInstrsReady() {
return interpreterContext;
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
return INTERPRET_METHOD(context, ensureInstrsReady(), getImplementationClass(), self, name, args, block);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args) {
return INTERPRET_METHOD(context, ensureInstrsReady(), getImplementationClass(), self, name, args, Block.NULL_BLOCK);
}

private IRubyObject INTERPRET_METHOD(ThreadContext context, InterpreterContext ic, RubyModule implClass,
IRubyObject self, String name, IRubyObject[] args, Block block) {
try {
ThreadContext.pushBacktrace(context, name, ic.getFileName(), context.getLine());
return ic.getEngine().interpret(context, null, self, ic, implClass, name, args, block);
} finally {
ThreadContext.popBacktrace(context);
}
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
return INTERPRET_METHOD(context, ensureInstrsReady(), getImplementationClass(), self, name, block);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name) {
return INTERPRET_METHOD(context, ensureInstrsReady(), getImplementationClass(), self, name, Block.NULL_BLOCK);
}

private IRubyObject INTERPRET_METHOD(ThreadContext context, InterpreterContext ic, RubyModule implClass,
IRubyObject self, String name, Block block) {
try {
ThreadContext.pushBacktrace(context, name, ic.getFileName(), context.getLine());
return ic.getEngine().interpret(context, null, self, ic, implClass, name, block);
} finally {
ThreadContext.popBacktrace(context);
}
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, Block block) {
return INTERPRET_METHOD(context, ensureInstrsReady(), getImplementationClass(), self, name, arg0, block);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0) {
return INTERPRET_METHOD(context, ensureInstrsReady(), getImplementationClass(), self, name, arg0, Block.NULL_BLOCK);
}

private IRubyObject INTERPRET_METHOD(ThreadContext context, InterpreterContext ic, RubyModule implClass,
IRubyObject self, String name, IRubyObject arg1, Block block) {
try {
ThreadContext.pushBacktrace(context, name, ic.getFileName(), context.getLine());
return ic.getEngine().interpret(context, null, self, ic, implClass, name, arg1, block);
} finally {
ThreadContext.popBacktrace(context);
}
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, Block block) {
return INTERPRET_METHOD(context, ensureInstrsReady(), getImplementationClass(), self, name, arg0, arg1, block);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1) {
return INTERPRET_METHOD(context, ensureInstrsReady(), getImplementationClass(), self, name, arg0, arg1, Block.NULL_BLOCK);
}

private IRubyObject INTERPRET_METHOD(ThreadContext context, InterpreterContext ic, RubyModule implClass,
IRubyObject self, String name, IRubyObject arg1, IRubyObject arg2, Block block) {
try {
ThreadContext.pushBacktrace(context, name, ic.getFileName(), context.getLine());
return ic.getEngine().interpret(context, null, self, ic, implClass, name, arg1, arg2, block);
} finally {
ThreadContext.popBacktrace(context);
}
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
return INTERPRET_METHOD(context, ensureInstrsReady(), getImplementationClass(), self, name, arg0, arg1, arg2, block);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
return INTERPRET_METHOD(context, ensureInstrsReady(), getImplementationClass(), self, name, arg0, arg1, arg2, Block.NULL_BLOCK);
}

private IRubyObject INTERPRET_METHOD(ThreadContext context, InterpreterContext ic, RubyModule implClass,
IRubyObject self, String name, IRubyObject arg1, IRubyObject arg2, IRubyObject arg3, Block block) {
try {
ThreadContext.pushBacktrace(context, name, ic.getFileName(), context.getLine());
return ic.getEngine().interpret(context, null, self, ic, implClass, name, arg1, arg2, arg3, block);
} finally {
ThreadContext.popBacktrace(context);
}
}
}
18 changes: 5 additions & 13 deletions core/src/main/java/org/jruby/ir/IRClosure.java
Original file line number Diff line number Diff line change
@@ -14,10 +14,8 @@
import org.jruby.ir.transformations.inlining.SimpleCloneInfo;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.ArgumentDescriptor;
import org.jruby.runtime.BlockBody;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.IRBlockBody;
import org.jruby.runtime.MixedModeIRBlockBody;
import org.jruby.runtime.AbstractIRBlockBody;
import org.jruby.runtime.InterpretedIRBlockBody;
import org.jruby.runtime.Signature;
import org.objectweb.asm.Handle;
@@ -43,7 +41,7 @@ public class IRClosure extends IRScope {
protected ArgumentDescriptor[] argDesc = ArgumentDescriptor.EMPTY_ARRAY;

/** Added for interp/JIT purposes */
private IRBlockBody body;
private AbstractIRBlockBody body;

/** Added for JIT purposes */
private Handle handle;
@@ -70,8 +68,7 @@ protected IRClosure(IRClosure c, IRScope lexicalParent, int closureId, String fu
if (getManager().isDryRun()) {
this.body = null;
} else {
boolean shouldJit = getManager().getInstanceConfig().getCompileMode().shouldJIT();
this.body = shouldJit ? new MixedModeIRBlockBody(c, c.getSignature()) : new InterpretedIRBlockBody(c, c.getSignature());
this.body = new InterpretedIRBlockBody(c, c.getSignature());
}

this.signature = c.signature;
@@ -105,8 +102,7 @@ public IRClosure(IRManager manager, IRScope lexicalParent, int lineNumber, Stati
if (getManager().isDryRun()) {
this.body = null;
} else {
boolean shouldJit = manager.getInstanceConfig().getCompileMode().shouldJIT();
this.body = shouldJit ? new MixedModeIRBlockBody(this, signature) : new InterpretedIRBlockBody(this, signature);
this.body = new InterpretedIRBlockBody(this, signature);
if (staticScope != null && !isBeginEndBlock) {
staticScope.setIRScope(this);
staticScope.setScopeType(this.getScopeType());
@@ -188,11 +184,7 @@ public boolean isFlipScope() {
return false;
}

public String toStringBody() {
return new StringBuilder(getName()).append(" = {\n").append(toStringInstrs()).append("\n}\n\n").toString();
}

public BlockBody getBlockBody() {
public AbstractIRBlockBody getBlockBody() {
return body;
}

15 changes: 3 additions & 12 deletions core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java
Original file line number Diff line number Diff line change
@@ -1403,12 +1403,8 @@ public static RubyModule newRubyClassFromIR(Ruby runtime, IRScope irClassBody, O
public static void defInterpretedClassMethod(ThreadContext context, IRScope method, IRubyObject obj) {
RubyClass rubyClass = checkClassForDef(context, method, obj);

DynamicMethod newMethod;
if (context.runtime.getInstanceConfig().getCompileMode() == RubyInstanceConfig.CompileMode.OFF) {
newMethod = new InterpretedIRMethod(method, Visibility.PUBLIC, rubyClass);
} else {
newMethod = new MixedModeIRMethod(method, Visibility.PUBLIC, rubyClass);
}
DynamicMethod newMethod = new MixedModeIRMethod(method, Visibility.PUBLIC, rubyClass);

// FIXME: needs checkID and proper encoding to force hard symbol
rubyClass.addMethod(method.getName(), newMethod);
if (!rubyClass.isRefinement()) {
@@ -1458,12 +1454,7 @@ public static void defInterpretedInstanceMethod(ThreadContext context, IRScope m
Visibility currVisibility = context.getCurrentVisibility();
Visibility newVisibility = Helpers.performNormalMethodChecksAndDetermineVisibility(runtime, rubyClass, method.getName(), currVisibility);

DynamicMethod newMethod;
if (context.runtime.getInstanceConfig().getCompileMode() == RubyInstanceConfig.CompileMode.OFF) {
newMethod = new InterpretedIRMethod(method, newVisibility, rubyClass);
} else {
newMethod = new MixedModeIRMethod(method, newVisibility, rubyClass);
}
DynamicMethod newMethod = new MixedModeIRMethod(method, newVisibility, rubyClass);

// FIXME: needs checkID and proper encoding to force hard symbol
Helpers.addInstanceMethod(rubyClass, method.getName(), newMethod, currVisibility, context, runtime);
Original file line number Diff line number Diff line change
@@ -12,32 +12,34 @@
import static org.jruby.RubyArray.newArray;
import static org.jruby.runtime.Helpers.arrayOf;

public abstract class IRBlockBody extends ContextAwareBlockBody {
public abstract class AbstractIRBlockBody extends ContextAwareBlockBody {
protected final String fileName;
protected final int lineNumber;
protected final IRClosure closure;
ThreadLocal<EvalType> evalType;

public IRBlockBody(IRScope closure, Signature signature) {
public AbstractIRBlockBody(IRScope closure, Signature signature) {
// ThreadLocal not set by default to avoid having many thread-local values initialized
// servers such as Tomcat tend to do thread-local checks when un-deploying apps,
// for JRuby leads to 100s of SEVERE warnings for a mid-size (booted) Rails app
this(closure, signature, new ThreadLocal());
}

/* internal */ IRBlockBody(IRScope closure, Signature signature, ThreadLocal evalType) {
/* internal */ AbstractIRBlockBody(IRScope closure, Signature signature, ThreadLocal evalType) {
super(closure.getStaticScope(), signature);
this.closure = (IRClosure) closure;
this.fileName = closure.getFileName();
this.lineNumber = closure.getLineNumber();
this.evalType = evalType;
}

@Override
public final EvalType getEvalType() {
final EvalType type = this.evalType.get();
return type == null ? EvalType.NONE : type;
}

@Override
public void setEvalType(final EvalType type) {
if (type == null || type == EvalType.NONE) {
this.evalType.remove();
@@ -46,6 +48,7 @@ public void setEvalType(final EvalType type) {
}
}

@Override
public IRubyObject yield(ThreadContext context, Block block, IRubyObject value, IRubyObject self, Block blockArg) {
if (canInvokeDirect()) {
return invokeYieldDirect(context, block, arrayOf(value), blockArg, self);
@@ -54,6 +57,7 @@ public IRubyObject yield(ThreadContext context, Block block, IRubyObject value,
}
}

@Override
public IRubyObject yield(ThreadContext context, Block block, IRubyObject[] args, IRubyObject self, Block blockArg) {
if (canInvokeDirect()) {
return invokeYieldDirect(context, block, args, blockArg, self);
@@ -87,52 +91,46 @@ public IRubyObject call(ThreadContext context, Block block, IRubyObject[] args,

@Override
public IRubyObject yieldSpecific(ThreadContext context, Block block) {
return invokeSpecific(context, block, Block.NULL_BLOCK, null);
return invokeYieldSpecific(context, block, Block.NULL_BLOCK, null);
}

@Override
public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0) {
return invokeSpecific(context, block, arg0, Block.NULL_BLOCK, null);
return invokeYieldSpecific(context, block, arg0, Block.NULL_BLOCK, null);
}

@Override
public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1) {
return invokeSpecific(context, block, arg0, arg1, Block.NULL_BLOCK, null);
return invokeYieldSpecific(context, block, arg0, arg1, Block.NULL_BLOCK, null);
}

@Override
public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
return invokeSpecific(context, block, arg0, arg1, arg2, Block.NULL_BLOCK, null);
return invokeYieldSpecific(context, block, arg0, arg1, arg2, Block.NULL_BLOCK, null);
}

protected abstract boolean canInvokeDirect();
protected boolean canInvokeDirect() {
return false;
}

/**
* This is the only method that *must* be implemented in blocks, and represents the bulk of logic.
*
* Other forms empty into this one and can be overridden to optimize specific paths.
*
* @param context
* @param block
* @param args
* @param blockArg
* @param self
* @return
*/
protected abstract IRubyObject invoke(ThreadContext context, Block block, IRubyObject[] args, Block blockArg, IRubyObject self);
protected IRubyObject invoke(ThreadContext context, Block block, IRubyObject[] args, Block blockArg, IRubyObject self) {
throw new UnsupportedOperationException("invoke not implemented");
}

protected abstract IRubyObject invokeCallDirect(ThreadContext context, Block block, IRubyObject[] args, Block blockArg, IRubyObject self);
protected IRubyObject invokeCallDirect(ThreadContext context, Block block, IRubyObject[] args, Block blockArg, IRubyObject self) {
throw new UnsupportedOperationException("invokeCallDirect not implemented");
}

protected abstract IRubyObject invokeYieldDirect(ThreadContext context, Block block, IRubyObject[] args, Block blockArg, IRubyObject self);
protected IRubyObject invokeYieldDirect(ThreadContext context, Block block, IRubyObject[] args, Block blockArg, IRubyObject self) {
throw new UnsupportedOperationException("invokeYieldDirect not implemented");
}

IRubyObject invokeYield(ThreadContext context, Block block, IRubyObject value, Block blockArg, IRubyObject self) {
if (block.isLambda()) return invokeLambda(context, block, value, blockArg, self);

int blockArity = signature.arityValue();

if (value == null) { // no args case from BlockBody.yieldSpecific
return invoke(context, block, IRubyObject.NULL_ARRAY, blockArg, self);
} else if (!signature.hasKwargs() && blockArity >= -1 && blockArity <= 1) {
if (!signature.hasKwargs() && blockArity >= -1 && blockArity <= 1) {
return invoke(context, block, arrayOf(value), blockArg, self);
} else {
return invoke(context, block, toAry(context, value), blockArg, self);
@@ -141,9 +139,7 @@ IRubyObject invokeYield(ThreadContext context, Block block, IRubyObject value, B

IRubyObject invokeLambda(ThreadContext context, Block block, IRubyObject value, Block blockArg, IRubyObject self) {
// Lambda does not splat arrays even if a rest arg is present when it wants a single parameter filled.
if (value == null) { // no args case from BlockBody.yieldSpecific
return invokeLambda(context, block, IRubyObject.NULL_ARRAY, blockArg, self);
} else if (signature.required() == 1 || signature.arityValue() == -1) {
if (signature.required() == 1 || signature.arityValue() == -1) {
return invokeLambda(context, block, arrayOf(value), blockArg, self);
} else {
return invokeLambda(context, block, toAry(context, value), blockArg, self);
@@ -156,7 +152,7 @@ IRubyObject invokeLambda(ThreadContext context, Block block, IRubyObject[] args,
return invoke(context, block, args, blockArg, self);
}

IRubyObject invokeSpecific(ThreadContext context, Block block, Block blockArg, IRubyObject self) {
IRubyObject invokeYieldSpecific(ThreadContext context, Block block, Block blockArg, IRubyObject self) {
if (canInvokeDirect()) {
return invokeYieldDirect(context, block, null, blockArg, self);
} else {
@@ -166,8 +162,8 @@ IRubyObject invokeSpecific(ThreadContext context, Block block, Block blockArg, I
}
}

IRubyObject invokeSpecific(ThreadContext context, Block block, IRubyObject arg0, Block blockArg, IRubyObject self) {
if (canInvokeDirect()) return invokeSpecificDirect(context, block, arg0, blockArg, self);
IRubyObject invokeYieldSpecific(ThreadContext context, Block block, IRubyObject arg0, Block blockArg, IRubyObject self) {
if (canInvokeDirect()) return invokeYieldSpecificDirect(context, block, arg0, blockArg, self);

if (arg0 instanceof RubyArray) {
// Unwrap the array arg
@@ -182,7 +178,7 @@ IRubyObject invokeSpecific(ThreadContext context, Block block, IRubyObject arg0,
return invokeYield(context, block, arg0, blockArg, self);
}

IRubyObject invokeSpecificDirect(ThreadContext context, Block block, IRubyObject arg0, Block blockArg, IRubyObject self) {
IRubyObject invokeYieldSpecificDirect(ThreadContext context, Block block, IRubyObject arg0, Block blockArg, IRubyObject self) {
IRubyObject[] args;
if (arg0 instanceof RubyArray) {
// Unwrap the array arg
@@ -193,37 +189,55 @@ IRubyObject invokeSpecificDirect(ThreadContext context, Block block, IRubyObject
return invokeYieldDirect(context, block, args, blockArg, self);
}

IRubyObject invokeSpecific(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, Block blockArg, IRubyObject self) {
if (canInvokeDirect()) return invokeSpecificDirect(context, block, arg0, arg1, blockArg, self);
IRubyObject invokeYieldSpecific(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, Block blockArg, IRubyObject self) {
if (canInvokeDirect()) return invokeYieldSpecificDirect(context, block, arg0, arg1, blockArg, self);

IRubyObject[] args = boxArgs(context, block, arg0, arg1);

return invoke(context, block, args, blockArg, self);
}

private IRubyObject[] boxArgs(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1) {
IRubyObject[] args;

switch (signature.arityValue()) {
case 0:
return invoke(context, block, IRubyObject.NULL_ARRAY, blockArg, self);
args = IRubyObject.NULL_ARRAY;
break;
case 1:
return invoke(context, block, arrayOf(newArray(context.runtime, arg0, arg1)), blockArg, self);
args = arrayOf(newArray(context.runtime, arg0, arg1));
break;
default:
IRubyObject[] args = arrayOf(arg0, arg1);
args = arrayOf(arg0, arg1);
if (block.isLambda()) signature.checkArity(context.runtime, args);
return invoke(context, block, args, blockArg, self);
}
return args;
}

IRubyObject invokeSpecific(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block blockArg, IRubyObject self) {
if (canInvokeDirect()) return invokeSpecificDirect(context, block, arg0, arg1, arg2, blockArg, self);
private IRubyObject[] boxArgs(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
IRubyObject[] args;

switch (signature.arityValue()) {
case 0:
return invoke(context, block, IRubyObject.NULL_ARRAY, blockArg, self);
args = IRubyObject.NULL_ARRAY;
break;
case 1:
return invoke(context, block, arrayOf(newArray(context.runtime, arg0, arg1, arg2)), blockArg, self);
args = arrayOf(newArray(context.runtime, arg0, arg1, arg2));
break;
default:
IRubyObject[] args = arrayOf(arg0, arg1, arg2);
args = arrayOf(arg0, arg1, arg2);
if (block.isLambda()) signature.checkArity(context.runtime, args);
return invoke(context, block, args, blockArg, self);
}
return args;
}

IRubyObject invokeYieldSpecific(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block blockArg, IRubyObject self) {
if (canInvokeDirect()) return invokeYieldSpecificDirect(context, block, arg0, arg1, arg2, blockArg, self);

return invoke(context, block, boxArgs(context, block, arg0, arg1, arg2), blockArg, self);
}

IRubyObject invokeSpecificDirect(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, Block blockArg, IRubyObject self) {
IRubyObject invokeYieldSpecificDirect(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, Block blockArg, IRubyObject self) {
switch (signature.arityValue()) {
case 0:
return invokeYieldDirect(context, block, arrayOf(arg0, arg1), blockArg, self);
@@ -234,7 +248,7 @@ IRubyObject invokeSpecificDirect(ThreadContext context, Block block, IRubyObject
}
}

IRubyObject invokeSpecificDirect(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block blockArg, IRubyObject self) {
IRubyObject invokeYieldSpecificDirect(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block blockArg, IRubyObject self) {
switch (signature.arityValue()) {
case 0:
return invokeYieldDirect(context, block, arrayOf(arg0, arg1, arg2), blockArg, self);
26 changes: 13 additions & 13 deletions core/src/main/java/org/jruby/runtime/BlockBody.java
Original file line number Diff line number Diff line change
@@ -77,32 +77,32 @@ public IRubyObject call(ThreadContext context, Block block, IRubyObject[] args,
return yield(context, block, prepareArgumentsForCall(context, args, block.type), null, blockArg);
}

public IRubyObject yieldSpecific(ThreadContext context, Block block) {
return yield(context, block, IRubyObject.NULL_ARRAY, null, Block.NULL_BLOCK);
}
public IRubyObject call(ThreadContext context, Block block, IRubyObject arg0, Block blockArg) {
return yield(context, block, prepareArgumentsForCall(context, arrayOf(arg0), block.type), null, blockArg);
}

public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0) {
return yield(context, block, arg0, null, Block.NULL_BLOCK);
public IRubyObject call(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, Block blockArg) {
return yield(context, block, prepareArgumentsForCall(context, arrayOf(arg0, arg1), block.type), null, blockArg);
}

public IRubyObject call(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, Block blockArg) {
return yield(context, block, prepareArgumentsForCall(context, new IRubyObject[] {arg0, arg1}, block.type), null, blockArg);
public IRubyObject call(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block blockArg) {
return yield(context, block, prepareArgumentsForCall(context, arrayOf(arg0, arg1, arg2), block.type), null, blockArg);
}

public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1) {
return yield(context, block, new IRubyObject[] { arg0, arg1 }, null, Block.NULL_BLOCK);
public IRubyObject yieldSpecific(ThreadContext context, Block block) {
return yield(context, block, IRubyObject.NULL_ARRAY, null, Block.NULL_BLOCK);
}

public IRubyObject call(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block blockArg) {
IRubyObject[] args = new IRubyObject[] {arg0, arg1, arg2};
return yield(context, block, prepareArgumentsForCall(context, args, block.type), null, blockArg);
public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0) {
return yield(context, block, arg0, null, Block.NULL_BLOCK);
}

public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1) {
return yield(context, block, arrayOf(arg0, arg1), null, Block.NULL_BLOCK);
}

public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
return yield(context, block, new IRubyObject[] { arg0, arg1, arg2 }, null, Block.NULL_BLOCK);
return yield(context, block, arrayOf(arg0, arg1, arg2), null, Block.NULL_BLOCK);
}

public abstract StaticScope getStaticScope();
54 changes: 53 additions & 1 deletion core/src/main/java/org/jruby/runtime/CompiledIRBlockBody.java
Original file line number Diff line number Diff line change
@@ -5,7 +5,9 @@

import java.lang.invoke.MethodHandle;

public class CompiledIRBlockBody extends IRBlockBody {
import static org.jruby.runtime.Helpers.arrayOf;

public class CompiledIRBlockBody extends AbstractIRBlockBody {
protected final MethodHandle handle;

public CompiledIRBlockBody(MethodHandle handle, IRScope closure, long encodedSignature) {
@@ -30,6 +32,56 @@ public MethodHandle getHandle() {
return handle;
}

@Override
public IRubyObject yield(ThreadContext context, Block block, IRubyObject value, IRubyObject self, Block blockArg) {
return invokeYieldDirect(context, block, arrayOf(value), blockArg, self);
}

@Override
public IRubyObject yield(ThreadContext context, Block block, IRubyObject[] args, IRubyObject self, Block blockArg) {
return invokeYieldDirect(context, block, args, blockArg, self);
}

@Override
public IRubyObject call(ThreadContext context, Block block, IRubyObject arg0, Block blockArg) {
return invokeCallDirect(context, block, arrayOf(arg0), blockArg, null);
}

@Override
public IRubyObject call(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, Block blockArg) {
return invokeCallDirect(context, block, arrayOf(arg0, arg1), blockArg, null);
}

@Override
public IRubyObject call(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block blockArg) {
return invokeCallDirect(context, block, arrayOf(arg0, arg1, arg2), blockArg, null);
}

@Override
public IRubyObject call(ThreadContext context, Block block, IRubyObject[] args, Block blockArg) {
return invokeCallDirect(context, block, args, blockArg, null);
}

@Override
public IRubyObject yieldSpecific(ThreadContext context, Block block) {
return invokeYieldDirect(context, block, null, Block.NULL_BLOCK, null);
}

@Override
public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0) {
return invokeYieldSpecificDirect(context, block, arg0, Block.NULL_BLOCK, null);
}

@Override
public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1) {
return invokeYieldSpecificDirect(context, block, arg0, arg1, Block.NULL_BLOCK, null);
}

@Override
public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
return invokeYieldSpecificDirect(context, block, arg0, arg1, arg2, Block.NULL_BLOCK, null);
}

@Override
protected IRubyObject invokeCallDirect(ThreadContext context, Block block, IRubyObject[] args, Block blockArg, IRubyObject self) {
context.setCurrentBlockType(Block.Type.PROC);
98 changes: 17 additions & 81 deletions core/src/main/java/org/jruby/runtime/InterpretedIRBlockBody.java
Original file line number Diff line number Diff line change
@@ -1,56 +1,24 @@
package org.jruby.runtime;

import java.io.ByteArrayOutputStream;
import org.jruby.RubyModule;
import org.jruby.compiler.Compilable;

import org.jruby.ir.IRClosure;
import org.jruby.ir.IRScope;
import org.jruby.ir.interpreter.Interpreter;
import org.jruby.ir.interpreter.InterpreterContext;
import org.jruby.ir.persistence.IRDumper;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.cli.Options;
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;

public class InterpretedIRBlockBody extends IRBlockBody implements Compilable<InterpreterContext> {
public class InterpretedIRBlockBody extends AbstractIRBlockBody {
private static final Logger LOG = LoggerFactory.getLogger(InterpretedIRBlockBody.class);
protected boolean pushScope;
protected boolean reuseParentScope;
private boolean displayedCFG = false; // FIXME: Remove when we find nicer way of logging CFG
private int callCount = 0;
private InterpreterContext interpreterContext;
private InterpreterContext fullInterpreterContext;

public InterpretedIRBlockBody(IRClosure closure, Signature signature) {
super(closure, signature);
this.pushScope = true;
this.reuseParentScope = false;

// JIT currently JITs blocks along with their method and no on-demand by themselves. We only
// promote to full build here if we are -X-C.
if (closure.getManager().getInstanceConfig().getCompileMode().shouldJIT() || Options.JIT_THRESHOLD.load() == -1) {
callCount = -1;
}
}

@Override
public void setCallCount(int callCount) {
this.callCount = callCount;
}

@Override
public void completeBuild(InterpreterContext interpreterContext) {
this.fullInterpreterContext = interpreterContext;
// This enables IR & CFG to be dumped in debug mode
// when this updated code starts executing.
this.displayedCFG = false;
}

@Override
public IRScope getIRScope() {
return closure;
}

@Override
@@ -59,65 +27,47 @@ public ArgumentDescriptor[] getArgumentDescriptors() {
}

public InterpreterContext ensureInstrsReady() {
if (IRRuntimeHelpers.isDebug() && !displayedCFG) {
LOG.info("Executing '" + closure + "' (pushScope=" + pushScope + ", reuseParentScope=" + reuseParentScope);
LOG.info(closure.debugOutput());
displayedCFG = true;
}
InterpreterContext interpreterContext = this.interpreterContext;

if (interpreterContext == null) {
if (IRRuntimeHelpers.isDebug()) {
LOG.info("Executing '" + closure + "' (pushScope=" + pushScope + ", reuseParentScope=" + reuseParentScope);
LOG.info(closure.debugOutput());
}

if (IRRuntimeHelpers.shouldPrintIR(closure.getStaticScope().getModule().getRuntime())) {
ByteArrayOutputStream baos = IRDumper.printIR(closure, false);

LOG.info("Printing simple IR for " + closure.getName() + ":\n" + new String(baos.toByteArray()));
}

interpreterContext = closure.getInterpreterContext();
fullInterpreterContext = interpreterContext;
interpreterContext = this.interpreterContext = closure.getInterpreterContext();
this.pushScope = interpreterContext.pushNewDynScope();
this.reuseParentScope = interpreterContext.reuseParentDynScope();
}
return interpreterContext;
}

@Override
public String getClassName(ThreadContext context) {
return null;
}

@Override
public String getName() {
return null;
return interpreterContext;
}

@Override
public boolean canInvokeDirect() {
return interpreterContext != null && interpreterContext.hasExplicitCallProtocol();
return false;
}

@Override
protected IRubyObject invokeCallDirect(ThreadContext context, Block block, IRubyObject[] args, Block blockArg, IRubyObject self) {
context.setCurrentBlockType(Block.Type.PROC);
InterpreterContext ic = ensureInstrsReady(); // so we get debugging output
return Interpreter.INTERPRET_BLOCK(context, block, null, ic, args, block.getBinding().getMethod(), blockArg);
throw new UnsupportedOperationException("invokeCallDirect not implemented");
}

@Override
protected IRubyObject invokeYieldDirect(ThreadContext context, Block block, IRubyObject[] args, Block blockArg, IRubyObject self) {
context.setCurrentBlockType(Block.Type.NORMAL);
InterpreterContext ic = ensureInstrsReady(); // so we get debugging output
return Interpreter.INTERPRET_BLOCK(context, block, self, ic, args, block.getBinding().getMethod(), blockArg);
throw new UnsupportedOperationException("invokeYieldDirect not implemented");
}

@Override
protected IRubyObject invoke(ThreadContext context, Block block, IRubyObject[] args, Block blockArg, IRubyObject self) {
if (callCount >= 0) promoteToFullBuild(context);

InterpreterContext ic = ensureInstrsReady();

// Update interpreter context for next time this block is executed
// This ensures that if we had determined canInvokeDirect() is false
// based on the old IC, we continue to execute with it.
interpreterContext = fullInterpreterContext;

Binding binding = block.getBinding();
Visibility oldVis = binding.getFrame().getVisibility();
Frame prevFrame = context.preYieldNoScope(binding);
@@ -126,9 +76,9 @@ protected IRubyObject invoke(ThreadContext context, Block block, IRubyObject[] a
// probably points to the parent scope? To be verified and fixed if necessary. There is no harm as it is now. It
// is just wasteful allocation since the scope is not used at all.
DynamicScope actualScope = binding.getDynamicScope();
if (ic.pushNewDynScope()) {
if (pushScope) {
context.pushScope(block.allocScope(actualScope));
} else if (ic.reuseParentDynScope()) {
} else if (reuseParentScope) {
// Reuse! We can avoid the push only if surrounding vars aren't referenced!
context.pushScope(actualScope);
}
@@ -143,18 +93,4 @@ protected IRubyObject invoke(ThreadContext context, Block block, IRubyObject[] a
}
}

// Unlike JIT in MixedMode this will always successfully build but if using executor pool it may take a while
// and replace interpreterContext asynchronously.
private void promoteToFullBuild(ThreadContext context) {
if (context.runtime.isBooting() && !Options.JIT_KERNEL.load()) return; // don't Promote to full build during runtime boot

if (callCount++ >= Options.JIT_THRESHOLD.load()) {
context.runtime.getJITCompiler().buildThresholdReached(context, this);
}
}

public RubyModule getImplementationClass() {
return null;
}

}
66 changes: 8 additions & 58 deletions core/src/main/java/org/jruby/runtime/MixedModeIRBlockBody.java
Original file line number Diff line number Diff line change
@@ -5,31 +5,26 @@
import org.jruby.compiler.Compilable;
import org.jruby.ir.IRClosure;
import org.jruby.ir.IRScope;
import org.jruby.ir.interpreter.Interpreter;
import org.jruby.ir.interpreter.InterpreterContext;
import org.jruby.ir.persistence.IRDumper;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.cli.Options;
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;

import java.io.ByteArrayOutputStream;

public class MixedModeIRBlockBody extends IRBlockBody implements Compilable<CompiledIRBlockBody> {
public class MixedModeIRBlockBody extends AbstractIRBlockBody implements Compilable<AbstractIRBlockBody> {
private static final Logger LOG = LoggerFactory.getLogger(MixedModeIRBlockBody.class);

protected boolean pushScope;
protected boolean reuseParentScope;
private boolean displayedCFG = false; // FIXME: Remove when we find nicer way of logging CFG
private volatile int callCount = 0;
private InterpreterContext interpreterContext;
private volatile CompiledIRBlockBody jittedBody;
private final InterpretedIRBlockBody baseBody;
private AbstractIRBlockBody jittedBody;

public MixedModeIRBlockBody(IRClosure closure, Signature signature) {
super(closure, signature);
this.pushScope = true;
this.reuseParentScope = false;
this.baseBody = new InterpretedIRBlockBody(closure, signature);

// JIT currently JITs blocks along with their method and no on-demand by themselves. We only
// promote to full build here if we are -X-C.
@@ -47,7 +42,7 @@ public void setEvalType(EvalType evalType) {

@Override
public boolean canInvokeDirect() {
return jittedBody != null || (interpreterContext != null && interpreterContext.hasExplicitCallProtocol());
return jittedBody != null;
}

@Override
@@ -58,7 +53,7 @@ public void setCallCount(int callCount) {
}

@Override
public void completeBuild(CompiledIRBlockBody blockBody) {
public void completeBuild(AbstractIRBlockBody blockBody) {
setCallCount(-1);
blockBody.evalType = this.evalType; // share with parent
this.jittedBody = blockBody;
@@ -79,22 +74,7 @@ public ArgumentDescriptor[] getArgumentDescriptors() {
}

public InterpreterContext ensureInstrsReady() {
if (IRRuntimeHelpers.isDebug() && !displayedCFG) {
LOG.info("Executing '" + closure + "' (pushScope=" + pushScope + ", reuseParentScope=" + reuseParentScope);
LOG.info(closure.debugOutput());
displayedCFG = true;
}

if (interpreterContext == null) {
if (IRRuntimeHelpers.shouldPrintIR(closure.getStaticScope().getModule().getRuntime())) {
ByteArrayOutputStream baos = IRDumper.printIR(closure, false);

LOG.info("Printing simple IR for " + closure.getName() + ":\n" + new String(baos.toByteArray()));
}

interpreterContext = closure.getInterpreterContext();
}
return interpreterContext;
return baseBody.ensureInstrsReady();
}

@Override
@@ -109,18 +89,12 @@ public String getName() {

@Override
protected IRubyObject invokeCallDirect(ThreadContext context, Block block, IRubyObject[] args, Block blockArg, IRubyObject self) {
// We should never get here if jittedBody is null
assert jittedBody != null : "direct call in MixedModeIRBlockBody without jitted body";

context.setCurrentBlockType(Block.Type.PROC);
return jittedBody.invokeCallDirect(context, block, args, blockArg, null);
}

@Override
protected IRubyObject invokeYieldDirect(ThreadContext context, Block block, IRubyObject[] args, Block blockArg, IRubyObject self) {
// We should never get here if jittedBody is null
assert jittedBody != null : "direct yield in MixedModeIRBlockBody without jitted body";

context.setCurrentBlockType(Block.Type.NORMAL);
return jittedBody.invokeYieldDirect(context, block, args, blockArg, self);
}
@@ -129,31 +103,7 @@ protected IRubyObject invokeYieldDirect(ThreadContext context, Block block, IRub
protected IRubyObject invoke(ThreadContext context, Block block, IRubyObject[] args, Block blockArg, IRubyObject self) {
if (callCount >= 0) promoteToFullBuild(context);

InterpreterContext ic = ensureInstrsReady();

Binding binding = block.getBinding();
Visibility oldVis = binding.getFrame().getVisibility();
Frame prevFrame = context.preYieldNoScope(binding);

// SSS FIXME: Maybe, we should allocate a NoVarsScope/DummyScope for for-loop bodies because the static-scope here
// probably points to the parent scope? To be verified and fixed if necessary. There is no harm as it is now. It
// is just wasteful allocation since the scope is not used at all.
DynamicScope actualScope = binding.getDynamicScope();
if (ic.pushNewDynScope()) {
context.pushScope(block.allocScope(actualScope));
} else if (ic.reuseParentDynScope()) {
// Reuse! We can avoid the push only if surrounding vars aren't referenced!
context.pushScope(actualScope);
}

self = IRRuntimeHelpers.updateBlockState(block, self);

try {
return Interpreter.INTERPRET_BLOCK(context, block, self, ic, args, binding.getMethod(), blockArg);
}
finally {
postYield(context, ic, binding, oldVis, prevFrame);
}
return baseBody.invoke(context, block, args, blockArg, self);
}

private void promoteToFullBuild(ThreadContext context) {
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package org.jruby.runtime;

import org.jruby.ir.IRClosure;
import org.jruby.ir.interpreter.Interpreter;
import org.jruby.ir.interpreter.InterpreterContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;

public class OptInterpretedIRBlockBody extends AbstractIRBlockBody {
private static final Logger LOG = LoggerFactory.getLogger(OptInterpretedIRBlockBody.class);
protected boolean pushScope;
protected boolean reuseParentScope;
private InterpreterContext fullInterpreterContext;

public OptInterpretedIRBlockBody(IRClosure closure, Signature signature) {
super(closure, signature);
this.pushScope = true;
this.reuseParentScope = false;
this.fullInterpreterContext = closure.prepareFullBuild();
}

@Override
public ArgumentDescriptor[] getArgumentDescriptors() {
return closure.getArgumentDescriptors();
}

@Override
public boolean canInvokeDirect() {
return true;
}

@Override
protected IRubyObject invokeCallDirect(ThreadContext context, Block block, IRubyObject[] args, Block blockArg, IRubyObject self) {
context.setCurrentBlockType(Block.Type.PROC);
InterpreterContext ic = fullInterpreterContext;
return Interpreter.INTERPRET_BLOCK(context, block, null, ic, args, block.getBinding().getMethod(), blockArg);
}

@Override
protected IRubyObject invokeYieldDirect(ThreadContext context, Block block, IRubyObject[] args, Block blockArg, IRubyObject self) {
context.setCurrentBlockType(Block.Type.NORMAL);
InterpreterContext ic = fullInterpreterContext;
return Interpreter.INTERPRET_BLOCK(context, block, self, ic, args, block.getBinding().getMethod(), blockArg);
}

@Override
protected IRubyObject invoke(ThreadContext context, Block block, IRubyObject[] args, Block blockArg, IRubyObject self) {
throw new UnsupportedOperationException("invoke not implemented");
}

}
2 changes: 2 additions & 0 deletions core/src/main/java/org/jruby/runtime/backtrace/FrameType.java
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
import org.jruby.internal.runtime.methods.InterpretedIRBodyMethod;
import org.jruby.internal.runtime.methods.InterpretedIRMethod;
import org.jruby.internal.runtime.methods.MixedModeIRMethod;
import org.jruby.internal.runtime.methods.OptInterpretedIRMethod;
import org.jruby.ir.interpreter.Interpreter;

public enum FrameType {
@@ -16,6 +17,7 @@ public enum FrameType {
INTERPRETED_CLASSES.add(Interpreter.class.getName());
INTERPRETED_CLASSES.add(MixedModeIRMethod.class.getName());
INTERPRETED_CLASSES.add(InterpretedIRMethod.class.getName());
INTERPRETED_CLASSES.add(OptInterpretedIRMethod.class.getName());
INTERPRETED_CLASSES.add(InterpretedIRBodyMethod.class.getName());
}

0 comments on commit b1b1644

Please sign in to comment.