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: 9afc3578ca53
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: e816e5aa971d
Choose a head ref
  • 2 commits
  • 10 files changed
  • 1 contributor

Commits on Dec 8, 2016

  1. Copy the full SHA
    cf1bc69 View commit details
  2. Mild refactoring of JITCompiler and added IR metrics.

    Moved all the inner classes to toplevel since this file was
    getting pretty big and shared more logic between the two bytecode
    generators.
    headius committed Dec 8, 2016
    Copy the full SHA
    e816e5a View commit details
70 changes: 70 additions & 0 deletions core/src/main/java/org/jruby/compiler/BlockJITClassGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/***** BEGIN LICENSE BLOCK *****
* Version: EPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby.compiler;

import org.jruby.Ruby;
import org.jruby.ir.interpreter.InterpreterContext;
import org.jruby.ir.targets.JVMVisitor;
import org.jruby.ir.targets.JVMVisitorMethodContext;
import org.jruby.runtime.MixedModeIRBlockBody;
import org.jruby.util.cli.Options;

public class BlockJITClassGenerator extends JITClassGenerator {
public BlockJITClassGenerator(String className, String methodName, String key, Ruby ruby, MixedModeIRBlockBody body, JVMVisitor visitor) {
super(className, methodName, key, ruby, visitor);
this.body = body;
}

@SuppressWarnings("unchecked")
protected void compile(JVMVisitorMethodContext context) {
if (bytecode != null) return;

// Time the compilation
long start = System.nanoTime();

InterpreterContext ic = body.ensureInstrsReady();

int insnCount = ic.getInstructions().length;
if (insnCount > Options.JIT_MAXSIZE.load()) {
// methods with more than our limit of basic blocks are likely too large to JIT, so bail out
throw new NotCompilableException("Could not compile " + body + "; instruction count " + insnCount + " exceeds threshold of " + Options.JIT_MAXSIZE.load());
}

// 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(body.getIRScope(), context);

compileTime = System.nanoTime() - start;
}

@Override
public String toString() {
return "{} at " + body.getFile() + ':' + body.getLine();
}

private final MixedModeIRBlockBody body;

}
98 changes: 98 additions & 0 deletions core/src/main/java/org/jruby/compiler/BlockJITTask.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/***** BEGIN LICENSE BLOCK *****
* Version: EPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby.compiler;

import org.jruby.ast.util.SexpMaker;
import org.jruby.ir.IRClosure;
import org.jruby.ir.targets.JVMVisitor;
import org.jruby.ir.targets.JVMVisitorMethodContext;
import org.jruby.runtime.CompiledIRBlockBody;
import org.jruby.runtime.MixedModeIRBlockBody;
import org.jruby.util.OneShotClassLoader;

class BlockJITTask implements Runnable {
private JITCompiler jitCompiler;
private final String className;
private final MixedModeIRBlockBody body;
private final String methodName;

public BlockJITTask(JITCompiler jitCompiler, MixedModeIRBlockBody body, String className) {
this.jitCompiler = jitCompiler;
this.body = body;
this.className = className;
this.methodName = body.getName();
}

public void run() {
try {
String key = SexpMaker.sha1(body.getIRScope());
JVMVisitor visitor = new JVMVisitor();
BlockJITClassGenerator generator = new BlockJITClassGenerator(className, methodName, key, jitCompiler.runtime, body, visitor);

JVMVisitorMethodContext context = new JVMVisitorMethodContext();
generator.compile(context);

// FIXME: reinstate active bytecode size check
// At this point we still need to reinstate the bytecode size check, to ensure we're not loading code
// 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 body sizes.
Class sourceClass = visitor.defineFromBytecode(body.getIRScope(), generator.bytecode(), new OneShotClassLoader(jitCompiler.runtime.getJRubyClassLoader()));

if (sourceClass == null) {
// class could not be found nor generated; give up on JIT and bail out
jitCompiler.counts.failCount.incrementAndGet();
return;
} else {
generator.updateCounters(jitCompiler.counts, body.ensureInstrsReady());
}

// successfully got back a jitted body

if (jitCompiler.config.isJitLogging()) {
JITCompiler.log(body.getImplementationClass(), body.getFile(), body.getLine(), className + "." + methodName, "done jitting");
}

String jittedName = context.getJittedName();

// blocks only have variable-arity
body.completeBuild(
new CompiledIRBlockBody(
JITCompiler.PUBLIC_LOOKUP.findStatic(sourceClass, jittedName, JVMVisitor.CLOSURE_SIGNATURE.type()),
body.getIRScope(),
((IRClosure) body.getIRScope()).getSignature().encode()));
} catch (Throwable t) {
if (jitCompiler.config.isJitLogging()) {
JITCompiler.log(body.getImplementationClass(), body.getFile(), body.getLine(), className + "." + methodName, "Could not compile; passes run: " + body.getIRScope().getExecutedPasses(), t.getMessage());
if (jitCompiler.config.isJitLoggingVerbose()) {
t.printStackTrace();
}
}

jitCompiler.counts.failCount.incrementAndGet();
}
}
}
34 changes: 34 additions & 0 deletions core/src/main/java/org/jruby/compiler/FullBuildTask.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.jruby.compiler;

import org.jruby.ir.interpreter.InterpreterContext;

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

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

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

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();
}
}
}
}
}
95 changes: 95 additions & 0 deletions core/src/main/java/org/jruby/compiler/JITClassGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/***** BEGIN LICENSE BLOCK *****
* Version: EPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby.compiler;

import org.jruby.Ruby;
import org.jruby.ir.interpreter.InterpreterContext;
import org.jruby.ir.targets.JVMVisitor;
import org.jruby.util.JavaNameMangler;
import org.jruby.util.cli.Options;

public class JITClassGenerator {
protected final String packageName;
protected final String className;
protected final String methodName;
protected final String digestString;
protected final JVMVisitor visitor;
protected final String name;
protected byte[] bytecode;
protected long compileTime;

public static final String CLASS_METHOD_DELIMITER = "$$";

public JITClassGenerator(String className, String methodName, String key, Ruby ruby, JVMVisitor visitor) {
this.methodName = methodName;
this.packageName = JITCompiler.RUBY_JIT_PREFIX;
this.visitor = visitor;
if (Options.COMPILE_INVOKEDYNAMIC.load()) {
// Some versions of Java 7 seems to have a bug that leaks definitions across cousin classloaders
// so we force the class name to be unique to this runtime.

// Also, invokedynamic forces us to make jitted bytecode unique to each runtime, since the call sites cache
// at class level rather than at our runtime level. This makes it impossible to share jitted bytecode
// across runtimes.

digestString = key + Math.abs(ruby.hashCode());
} else {
digestString = key;
}
this.className = packageName + '/' + className.replace('.', '/') + CLASS_METHOD_DELIMITER + JavaNameMangler.mangleMethodName(methodName) + '_' + digestString;
this.name = this.className.replace('/', '.');
}

void updateCounters(JITCounts counts, InterpreterContext ic) {
counts.compiledCount.incrementAndGet();
counts.compileTime.addAndGet(compileTime);
counts.codeSize.addAndGet(bytecode.length);
int insnCount = ic.getInstructions().length;
counts.irSize.addAndGet(insnCount);
counts.compileTimeAverage.set(counts.compileTime.get() / counts.compiledCount.get());
counts.codeAverageSize.set(counts.codeSize.get() / counts.compiledCount.get());
counts.irAverageSize.set(counts.irSize.get() / counts.compiledCount.get());
synchronized (counts) {
long largest;
for (;;) {
largest = counts.codeLargestSize.get();
if (largest >= bytecode.length || counts.codeLargestSize.compareAndSet(largest, bytecode.length)) break;
}
for (;;) {
largest = counts.irLargestSize.get();
if (largest >= insnCount || counts.irLargestSize.compareAndSet(largest, insnCount)) break;
}
}
}

public byte[] bytecode() {
return bytecode;
}

public String name() {
return name;
}
}
Loading