Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 851477a429de
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: a1e06a27f544
Choose a head ref
  • 2 commits
  • 9 files changed
  • 1 contributor

Commits on May 15, 2015

  1. push IRScope to base class for block bodies (will make it easier to c…

    …onvert blocks in define_method to full methods later)
    enebo committed May 15, 2015
    Copy the full SHA
    22e2ed7 View commit details
  2. Combine full build and JIT executor code. This will help prevent chan…

    …ges over time of having two
    
    code paths (this combination fixed -X-C to honor no background building it jit.threshold=0).
    enebo committed May 15, 2015
    Copy the full SHA
    a1e06a2 View commit details
Original file line number Diff line number Diff line change
@@ -3,15 +3,17 @@
import org.jruby.RubyModule;
import org.jruby.ir.IRScope;
import org.jruby.ir.interpreter.InterpreterContext;
import org.jruby.runtime.ThreadContext;

/**
* Blocks and methods both share same full build mechanism so they implement this to be buildable.
*/
public interface FullBuildSource {
public interface Compilable<T> {
public void setCallCount(int count);
public void switchToFullBuild(InterpreterContext interpreterContext);
public void completeBuild(T buildResult);
public IRScope getIRScope();
public InterpreterContext ensureInstrsReady();
public String getClassName(ThreadContext context);
public String getName();
public String getFile();
public int getLine();
93 changes: 34 additions & 59 deletions core/src/main/java/org/jruby/compiler/JITCompiler.java
Original file line number Diff line number Diff line change
@@ -137,49 +137,30 @@ public long getLargestCodeSize() {
}

public void tearDown() {
if (executor != null) {
try {
executor.shutdown();
} catch (SecurityException se) {
// ignore, can't shut down executor
}
try {
executor.shutdown();
} catch (SecurityException se) {
// ignore, can't shut down executor
}
}

public void fullBuildThresholdReached(final FullBuildSource method, final RubyInstanceConfig config) {
// Disable any other jit tasks from entering queue
method.setCallCount(-1);

Runnable jitTask = new FullBuildTask(method);

if (config.getJitThreshold() > 0) {
if (config.getJitBackground() && executor != null) {
try {
executor.submit(jitTask);
} catch (RejectedExecutionException ree) {
// failed to submit, just run it directly
jitTask.run();
}
} else {
// Because are non-asynchonously build if the JIT threshold happens to be 0 we will have no ic yet.
method.ensureInstrsReady();
// just run directly
jitTask.run();
}
public Runnable getTaskFor(ThreadContext context, Compilable method) {
if (method instanceof MixedModeIRMethod) {
return new JITTask((MixedModeIRMethod) method, method.getClassName(context));
}

return new FullBuildTask(method);
}

public void jitThresholdReached(final MixedModeIRMethod method, final RubyInstanceConfig config, ThreadContext context, final String className, final String methodName) {

public void buildThresholdReached(ThreadContext context, final Compilable method) {
final RubyInstanceConfig config = context.runtime.getInstanceConfig();

// Disable any other jit tasks from entering queue
method.setCallCount(-1);

Runnable jitTask = new JITTask(className, method, methodName);

// if background JIT is enabled and threshold is > 0 and we have an executor...
if (config.getJitBackground() &&
config.getJitThreshold() > 0 &&
executor != null) {
// JIT in background
Runnable jitTask = getTaskFor(context, method);

if (config.getJitBackground() && config.getJitThreshold() > 0) {
try {
executor.submit(jitTask);
} catch (RejectedExecutionException ree) {
@@ -189,7 +170,6 @@ public void jitThresholdReached(final MixedModeIRMethod method, final RubyInstan
} else {
// Because are non-asynchonously build if the JIT threshold happens to be 0 we will have no ic yet.
method.ensureInstrsReady();

// just run directly
jitTask.run();
}
@@ -198,15 +178,15 @@ public void jitThresholdReached(final MixedModeIRMethod method, final RubyInstan
private static final MethodHandles.Lookup PUBLIC_LOOKUP = MethodHandles.publicLookup().in(Ruby.class);

private class FullBuildTask implements Runnable {
private final FullBuildSource method;
private final Compilable method;

public FullBuildTask(FullBuildSource method) {
public FullBuildTask(Compilable method) {
this.method = method;
}

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

if (config.isJitLogging()) {
log(method.getImplementationClass(), method.getFile(), method.getLine(), method.getName(), "done building");
@@ -228,10 +208,10 @@ private class JITTask implements Runnable {
private final MixedModeIRMethod method;
private final String methodName;

public JITTask(String className, MixedModeIRMethod method, String methodName) {
this.className = className;
public JITTask(MixedModeIRMethod method, String className) {
this.method = method;
this.methodName = methodName;
this.className = className;
this.methodName = method.getName();
}

public void run() {
@@ -258,7 +238,7 @@ public void run() {
}
}

String key = SexpMaker.sha1(method.getIRMethod());
String key = SexpMaker.sha1(method.getIRScope());
JVMVisitor visitor = new JVMVisitor();
JITClassGenerator generator = new JITClassGenerator(className, methodName, key, runtime, method, visitor);

@@ -269,7 +249,7 @@ public void run() {
// that's so big that JVMs won't even try to compile it. Removed the check because with the new IR JIT
// bytecode counts often include all nested scopes, even if they'd be different methods. We need a new
// mechanism of getting all method sizes.
Class sourceClass = visitor.defineFromBytecode(method.getIRMethod(), generator.bytecode(), new OneShotClassLoader(runtime.getJRubyClassLoader()));
Class sourceClass = visitor.defineFromBytecode(method.getIRScope(), generator.bytecode(), new OneShotClassLoader(runtime.getJRubyClassLoader()));

if (sourceClass == null) {
// class could not be found nor generated; give up on JIT and bail out
@@ -293,47 +273,44 @@ public void run() {
log(method.getImplementationClass(), method.getFile(), method.getLine(), className + "." + methodName, "done jitting");
}

Map<Integer, MethodType> signatures = ((IRMethod)method.getIRMethod()).getNativeSignatures();
String jittedName = ((IRMethod)method.getIRMethod()).getJittedName();
Map<Integer, MethodType> signatures = ((IRMethod)method.getIRScope()).getNativeSignatures();
String jittedName = ((IRMethod)method.getIRScope()).getJittedName();
if (signatures.size() == 1) {
// only variable-arity
method.switchToJitted(
method.completeBuild(
new CompiledIRMethod(
PUBLIC_LOOKUP.findStatic(sourceClass, jittedName, signatures.get(-1)),
method.getIRMethod(),
method.getIRScope(),
method.getVisibility(),
method.getImplementationClass(),
method.getIRMethod().receivesKeywordArgs()));
method.getIRScope().receivesKeywordArgs()));

} else {
// also specific-arity
for (Map.Entry<Integer, MethodType> entry : signatures.entrySet()) {
if (entry.getKey() == -1) continue; // variable arity handle pushed above

method.switchToJitted(
method.completeBuild(
new CompiledIRMethod(
PUBLIC_LOOKUP.findStatic(sourceClass, jittedName, signatures.get(-1)),
PUBLIC_LOOKUP.findStatic(sourceClass, jittedName, entry.getValue()),
entry.getKey(),
method.getIRMethod(),
method.getIRScope(),
method.getVisibility(),
method.getImplementationClass(),
method.getIRMethod().receivesKeywordArgs()));
method.getIRScope().receivesKeywordArgs()));
break;
}
}

return;
} catch (Throwable t) {
if (config.isJitLogging()) {
log(method.getImplementationClass(), method.getFile(), method.getLine(), className + "." + methodName, "Could not compile; passes run: " + method.getIRMethod().getExecutedPasses(), t.getMessage());
log(method.getImplementationClass(), method.getFile(), method.getLine(), className + "." + methodName, "Could not compile; passes run: " + method.getIRScope().getExecutedPasses(), t.getMessage());
if (config.isJitLoggingVerbose()) {
t.printStackTrace();
}
}

counts.failCount.incrementAndGet();
return;
}
}
}
@@ -375,7 +352,6 @@ public JITClassGenerator(String className, String methodName, String key, Ruby r
this.className = packageName + "/" + className.replace('.', '/') + CLASS_METHOD_DELIMITER + JavaNameMangler.mangleMethodName(methodName) + "_" + digestString;
this.name = this.className.replaceAll("/", ".");
this.methodName = methodName;
this.ruby = ruby;
this.method = method;
this.visitor = visitor;
}
@@ -391,7 +367,7 @@ protected void compile() {

// This may not be ok since we'll end up running passes specific to JIT
// CON FIXME: Really should clone scope before passes in any case
bytecode = visitor.compileToBytecode(method.getIRMethod());
bytecode = visitor.compileToBytecode(method.getIRScope());

compileTime = System.nanoTime() - start;
}
@@ -426,7 +402,6 @@ public String toString() {
return methodName + "() at " + method.getFile() + ":" + method.getLine();
}

private final Ruby ruby;
private final String packageName;
private final String className;
private final String methodName;
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@

import org.jruby.Ruby;
import org.jruby.RubyModule;
import org.jruby.compiler.FullBuildSource;
import org.jruby.compiler.Compilable;
import org.jruby.ir.IRMethod;
import org.jruby.ir.IRScope;
import org.jruby.ir.interpreter.InterpreterContext;
@@ -24,7 +24,7 @@
/**
* Method for -X-C (interpreted only execution). See MixedModeIRMethod for inter/JIT method impl.
*/
public class InterpretedIRMethod extends DynamicMethod implements IRMethodArgs, PositionAware, FullBuildSource {
public class InterpretedIRMethod extends DynamicMethod implements IRMethodArgs, PositionAware, Compilable<InterpreterContext> {
private static final Logger LOG = LoggerFactory.getLogger("InterpretedIRMethod");

private Signature signature;
@@ -58,7 +58,7 @@ public StaticScope getStaticScope() {

public ArgumentDescriptor[] getArgumentDescriptors() {
ensureInstrsReady(); // Make sure method is minimally built before returning this info
return ((IRMethod) method).getArgumentDescriptors();
return method.getArgumentDescriptors();
}

public Signature getSignature() {
@@ -256,7 +256,7 @@ protected void doDebug() {
}
}

public void switchToFullBuild(InterpreterContext interpreterContext) {
public void completeBuild(InterpreterContext interpreterContext) {
this.interpreterContext = interpreterContext;
}

@@ -265,19 +265,20 @@ public void switchToFullBuild(InterpreterContext interpreterContext) {
protected void promoteToFullBuild(ThreadContext context) {
Ruby runtime = context.runtime;

// don't Promote to full build during runtime boot
if (runtime.isBooting()) return;
if (runtime.isBooting()) return; // don't Promote to full build during runtime boot

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

@Override
public DynamicMethod dup() {
return new InterpretedIRMethod(method, visibility, implementationClass);
}

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

public String getFile() {
return method.getFileName();
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package org.jruby.internal.runtime.methods;

import org.jruby.MetaClass;
import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyModule;
import org.jruby.compiler.Compilable;
import org.jruby.ir.*;
import org.jruby.ir.interpreter.InterpreterContext;
import org.jruby.ir.runtime.IRRuntimeHelpers;
@@ -21,7 +21,7 @@
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;

public class MixedModeIRMethod extends DynamicMethod implements IRMethodArgs, PositionAware {
public class MixedModeIRMethod extends DynamicMethod implements IRMethodArgs, PositionAware, Compilable<DynamicMethod> {
private static final Logger LOG = LoggerFactory.getLogger("InterpretedIRMethod");

private Signature signature;
@@ -49,18 +49,14 @@ public MixedModeIRMethod(IRScope method, Visibility visibility, RubyModule imple
}
}

public IRScope getIRMethod() {
public IRScope getIRScope() {
return method;
}

public DynamicMethod getActualMethod() {
return box.actualMethod;
}

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

public StaticScope getStaticScope() {
return method.getStaticScope();
}
@@ -303,20 +299,21 @@ public DynamicMethod getMethodForCaching() {
return this;
}

public void switchToJitted(CompiledIRMethod newMethod) {
@Override
public void completeBuild(DynamicMethod newMethod) {
this.box.actualMethod = newMethod;
this.box.actualMethod.serialNumber = this.serialNumber;
this.box.callCount = -1;
getImplementationClass().invalidateCacheDescendants();
}


protected void tryJit(ThreadContext context, DynamicMethodBox box) {
Ruby runtime = context.runtime;
if (context.runtime.isBooting()) return; // don't JIT during runtime boot

// don't JIT during runtime boot
if (runtime.isBooting()) return;
if (box.callCount++ >= Options.JIT_THRESHOLD.load()) context.runtime.getJITCompiler().buildThresholdReached(context, this);
}

public String getClassName(ThreadContext context) {
String className;
if (implementationClass.isSingleton()) {
MetaClass metaClass = (MetaClass)implementationClass;
@@ -333,11 +330,7 @@ protected void tryJit(ThreadContext context, DynamicMethodBox box) {
// use the class name
className = implementationClass.getName();
}


if (box.callCount++ >= Options.JIT_THRESHOLD.load()) {
context.runtime.getJITCompiler().jitThresholdReached(this, context.runtime.getInstanceConfig(), context, className, name);
}
return className;
}

public void setActualMethod(CompiledIRMethod method) {
@@ -363,4 +356,8 @@ public String getFile() {
public int getLine() {
return method.getLineNumber();
}

public void setCallCount(int callCount) {
box.callCount = callCount;
}
}
1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/ir/IRScope.java
Original file line number Diff line number Diff line change
@@ -569,6 +569,7 @@ public synchronized FullInterpreterContext prepareFullBuild() {
if (!isUnsafeScope()) new AddCallProtocolInstructions().run(this);

fullInterpreterContext.generateInstructionsForIntepretation();
System.out.println("FOC : " + fullInterpreterContext.toStringInstrs());
return fullInterpreterContext;
}

2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/ir/interpreter/Profiler.java
Original file line number Diff line number Diff line change
@@ -160,7 +160,7 @@ public int compare(IRCallSite a, IRCallSite b) {
IRScope hc = isHotClosure ? hs : null;
hs = isHotClosure ? hs.getLexicalParent() : hs;

IRScope tgtMethod = ircs.tgtM.getIRMethod();
IRScope tgtMethod = ircs.tgtM.getIRScope();

Instr[] instrs = tgtMethod.getInterpreterContext().getInstructions();
// Dont inline large methods -- 500 is arbitrary
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package org.jruby.runtime;

import org.jruby.EvalType;
import org.jruby.ir.IRClosure;
import org.jruby.ir.IRFlags;
import org.jruby.ir.IRMethod;
import org.jruby.ir.IRScope;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.runtime.Block.Type;
@@ -12,16 +10,14 @@
import java.lang.invoke.MethodHandle;

public class CompiledIRBlockBody extends IRBlockBody {
protected final IRClosure closure;
protected final MethodHandle handle;
protected boolean pushScope;
protected boolean reuseParentScope;
protected boolean usesKwargs;

public CompiledIRBlockBody(MethodHandle handle, IRScope closure, long encodedSignature) {
super(closure.getStaticScope(), closure.getFileName(), closure.getLineNumber(), Signature.decode(encodedSignature));
super(closure, Signature.decode(encodedSignature));
this.handle = handle;
this.closure = (IRClosure)closure;
// FIXME: duplicated from InterpreterContext
this.reuseParentScope = closure.getFlags().contains(IRFlags.REUSE_PARENT_DYNSCOPE);
this.pushScope = !closure.getFlags().contains(IRFlags.DYNSCOPE_ELIMINATED) && !this.reuseParentScope;
12 changes: 7 additions & 5 deletions core/src/main/java/org/jruby/runtime/IRBlockBody.java
Original file line number Diff line number Diff line change
@@ -2,20 +2,22 @@

import org.jruby.EvalType;
import org.jruby.RubyArray;
import org.jruby.ir.IRScope;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.Block.Type;
import org.jruby.runtime.builtin.IRubyObject;

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

public IRBlockBody(StaticScope staticScope, String fileName, int lineNumber, Signature signature) {
super(staticScope, signature);
this.fileName = fileName;
this.lineNumber = lineNumber;
public IRBlockBody(IRScope closure, Signature signature) {
super(closure.getStaticScope(), signature);
this.closure = closure;
this.fileName = closure.getFileName();
this.lineNumber = closure.getLineNumber();
this.evalType = new ThreadLocal();
this.evalType.set(EvalType.NONE);
}
25 changes: 11 additions & 14 deletions core/src/main/java/org/jruby/runtime/InterpretedIRBlockBody.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package org.jruby.runtime;

import org.jruby.EvalType;
import org.jruby.Ruby;
import org.jruby.RubyInstanceConfig;
import org.jruby.RubyModule;
import org.jruby.compiler.FullBuildSource;
import org.jruby.compiler.Compilable;
import org.jruby.ir.IRClosure;
import org.jruby.ir.IRScope;
import org.jruby.ir.interpreter.Interpreter;
@@ -16,18 +15,16 @@
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;

public class InterpretedIRBlockBody extends IRBlockBody implements FullBuildSource {
public class InterpretedIRBlockBody extends IRBlockBody implements Compilable<InterpreterContext> {
private static final Logger LOG = LoggerFactory.getLogger("InterpretedIRBlockBody");
protected final IRClosure closure;
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;

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

@@ -44,7 +41,7 @@ public void setCallCount(int callCount) {
}

@Override
public void switchToFullBuild(InterpreterContext interpreterContext) {
public void completeBuild(InterpreterContext interpreterContext) {
this.interpreterContext = interpreterContext;
}

@@ -71,6 +68,11 @@ public InterpreterContext ensureInstrsReady() {
return interpreterContext;
}

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

@Override
public String getName() {
return null;
@@ -129,14 +131,9 @@ protected IRubyObject commonYieldPath(ThreadContext context, IRubyObject[] args,
// Unlike JIT in MixedMode this will always successfully build but if using executor pool it may take a while
// and replace interpreterContext asynchronously.
protected void promoteToFullBuild(ThreadContext context) {
Ruby runtime = context.runtime;

// don't Promote to full build during runtime boot
if (runtime.isBooting()) return;
if (context.runtime.isBooting()) return; // don't Promote to full build during runtime boot

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

public RubyModule getImplementationClass() {