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

Commits on Dec 7, 2015

  1. In middle of debug but capturing calleriSite.call = null and couple o…

    …f other ugly hacks towards getting JIT to inline
    enebo committed Dec 7, 2015
    Copy the full SHA
    f8b0d6f View commit details

Commits on Dec 12, 2015

  1. Copy the full SHA
    faa115b View commit details
9 changes: 6 additions & 3 deletions core/src/main/java/org/jruby/compiler/JITCompiler.java
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@
import org.jruby.RubyModule;
import org.jruby.ast.util.SexpMaker;
import org.jruby.internal.runtime.methods.CompiledIRMethod;
import org.jruby.internal.runtime.methods.DynamicMethod;
import org.jruby.internal.runtime.methods.MixedModeIRMethod;
import org.jruby.ir.IRClosure;
import org.jruby.ir.interpreter.InterpreterContext;
@@ -404,7 +405,7 @@ public static String getHashForBytes(byte[] bytes) {
}

public static class MethodJITClassGenerator {
public MethodJITClassGenerator(String className, String methodName, String key, Ruby ruby, MixedModeIRMethod method, JVMVisitor visitor) {
public MethodJITClassGenerator(String className, String methodName, String key, Ruby ruby, Compilable method, JVMVisitor visitor) {
this.packageName = JITCompiler.RUBY_JIT_PREFIX;
if (RubyInstanceConfig.JAVA_VERSION == Opcodes.V1_7 || Options.COMPILE_INVOKEDYNAMIC.load() == true) {
// Some versions of Java 7 seems to have a bug that leaks definitions across cousin classloaders
@@ -426,7 +427,7 @@ public MethodJITClassGenerator(String className, String methodName, String key,
}

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

// Time the compilation
@@ -440,6 +441,8 @@ protected void compile(JVMVisitorMethodContext context) {
throw new NotCompilableException("Could not compile " + method + "; instruction count " + insnCount + " exceeds threshold of " + Options.JIT_MAXSIZE.load());
}

method.getIRScope().setCompilable((DynamicMethod)method);
System.out.println("Adding compiable to " + method.getIRScope());
// 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.getIRScope(), context);
@@ -482,7 +485,7 @@ public String toString() {
private final String className;
private final String methodName;
private final String digestString;
private final MixedModeIRMethod method;
private final Compilable method;
private final JVMVisitor visitor;

private byte[] bytecode;
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package org.jruby.internal.runtime.methods;

import org.jruby.MetaClass;
import org.jruby.RubyClass;
import org.jruby.RubyModule;
import org.jruby.compiler.Compilable;
import org.jruby.ir.IRMethod;
import org.jruby.ir.IRScope;
import org.jruby.ir.interpreter.InterpreterContext;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.ArgumentDescriptor;
@@ -18,10 +22,10 @@

import org.jruby.runtime.Helpers;

public class CompiledIRMethod extends DynamicMethod implements IRMethodArgs, PositionAware {
protected final MethodHandle variable;
public class CompiledIRMethod extends DynamicMethod implements IRMethodArgs, PositionAware, Compilable<DynamicMethod> {
public MethodHandle variable;

protected final MethodHandle specific;
public MethodHandle specific;
protected final int specificArity;

protected final IRScope method;
@@ -241,11 +245,56 @@ public DynamicMethod dup() {
return new CompiledIRMethod(variable, specific, specificArity, method, getVisibility(), implementationClass, hasKwargs);
}

@Override
public void setCallCount(int count) {

}

@Override
public void completeBuild(DynamicMethod buildResult) {

}

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

@Override
public InterpreterContext ensureInstrsReady() {
return getIRMethod().getFullInterpreterContext();
}

@Override
public String getClassName(ThreadContext context) {
String className;
if (implementationClass.isSingleton()) {
MetaClass metaClass = (MetaClass) implementationClass;
RubyClass realClass = metaClass.getRealClass();
// if real class is Class
if (realClass == context.runtime.getClassClass()) {
// use the attached class's name
className = ((RubyClass) metaClass.getAttached()).getName();
} else {
// use the real class name
className = realClass.getName();
}
} else {
// use the class name
className = implementationClass.getName();
}
return className;
}

public String getFile() {
return method.getFileName();
}

public int getLine() {
return method.getLineNumber();
}

@Override
public void setInterpreterContext(InterpreterContext interpreterContext) {
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.jruby.internal.runtime.methods;

import org.jruby.ir.interpreter.InterpreterContext;
import org.jruby.runtime.ArgumentDescriptor;
import org.jruby.runtime.Signature;

86 changes: 83 additions & 3 deletions core/src/main/java/org/jruby/ir/IRScope.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
package org.jruby.ir;

import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import org.jruby.ParseResult;
import org.jruby.Ruby;
import org.jruby.RubyInstanceConfig;
import org.jruby.RubyModule;
import org.jruby.ast.util.SexpMaker;
import org.jruby.compiler.Compilable;
import org.jruby.compiler.JITCompiler;
import org.jruby.compiler.JITCompiler.MethodJITClassGenerator;
import org.jruby.internal.runtime.methods.CompiledIRMethod;
import org.jruby.internal.runtime.methods.DynamicMethod;
import org.jruby.ir.dataflow.analyses.LiveVariablesProblem;
import org.jruby.ir.dataflow.analyses.StoreLocalVarPlacementProblem;
import org.jruby.ir.dataflow.analyses.UnboxableOpsAnalysisProblem;
@@ -16,13 +24,16 @@
import org.jruby.ir.passes.*;
import org.jruby.ir.representations.BasicBlock;
import org.jruby.ir.representations.CFG;
import org.jruby.ir.targets.JVMVisitor;
import org.jruby.ir.targets.JVMVisitorMethodContext;
import org.jruby.ir.transformations.inlining.CFGInliner;
import org.jruby.ir.transformations.inlining.SimpleCloneInfo;
import org.jruby.parser.StaticScope;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

import org.jruby.util.OneShotClassLoader;
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;

@@ -123,7 +134,7 @@ public abstract class IRScope implements ParseResult {

private TemporaryVariable yieldClosureVariable;

private Compilable compilable;
private DynamicMethod compilable;

// Used by cloning code
protected IRScope(IRScope s, IRScope lexicalParent) {
@@ -543,13 +554,24 @@ public void prepareFullBuildCommon() {

fullInterpreterContext = new FullInterpreterContext(this, instrs);
}

// FIXME: This is a hack so JIT Compiler can store method in live scope so we can more easily extract it.
// The existence of compilable as a field feels wrong in this class.
public void setCompilable(DynamicMethod compilable) {
this.compilable = compilable;
}

public DynamicMethod getCompilable() {
return compilable;
}

/**
* This initializes a more complete(full) InterpreterContext which if used in mixed mode will be
* used by the JIT and if used in pure-interpreted mode it will be used by an interpreter engine.
*/
public synchronized FullInterpreterContext prepareFullBuild(Compilable compilable) {
// FIXME: This is gross
this.compilable = compilable;
this.compilable = (DynamicMethod) compilable;
// Don't run if same method was queued up in the tiny race for scheduling JIT/Full Build OR
// for any nested closures which got a a fullInterpreterContext but have not run any passes
// or generated instructions.
@@ -1027,10 +1049,68 @@ public void inlineMethod(Compilable method, RubyModule implClass, int classToken
this.fullInterpreterContext = newContext;

System.out.println(fullInterpreterContext.toStringInstrs());
compilable.setInterpreterContext(fullInterpreterContext);
((Compilable) compilable).setInterpreterContext(fullInterpreterContext);
// Since inline is an if/else of logic in this version of inlining we will just replace the FIC.
}

// FIXME: Passing in DynamicMethod is gross here we probably can minimally cast to Compilable
public void inlineMethodJIT(Compilable method, RubyModule implClass, int classToken, BasicBlock basicBlock, CallBase call, boolean cloneHost) {
IRMethod methodToInline = (IRMethod) method.getIRScope();

// We need fresh fic so we can modify it during inlining without making already running code explode.
FullInterpreterContext newContext = fullInterpreterContext.duplicate();

new CFGInliner(newContext).inlineMethod(methodToInline, implClass, classToken, basicBlock, call, cloneHost);

// Reset state
resetState();

// Re-run opts
for (CompilerPass pass: getManager().getInliningCompilerPasses(this)) {
pass.run(this);
}


//runCompilerPasses(getManager().getJITPasses(this));
newContext.generateInstructionsForIntepretation();
this.fullInterpreterContext = newContext;

System.out.println("FFFFFF\n" + fullInterpreterContext.toStringInstrs());
// ((Compilable) compilable).setInterpreterContext(fullInterpreterContext);
// Since inline is an if/else of logic in this version of inlining we will just replace the FIC.

Ruby runtime = implClass.getRuntime();
String key = SexpMaker.sha1(this);
JVMVisitor visitor = new JVMVisitor();
MethodJITClassGenerator generator = new MethodJITClassGenerator(implClass.getName(), getName(), key, runtime, (Compilable) compilable, visitor);

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

Class sourceClass = visitor.defineFromBytecode(this, generator.bytecode(), new OneShotClassLoader(runtime.getJRubyClassLoader()));

Map<Integer, MethodType> signatures = context.getNativeSignatures();
String jittedName = context.getJittedName();
try {
if (signatures.size() == 1) {
((CompiledIRMethod) compilable).variable = MethodHandles.publicLookup().findStatic(sourceClass, jittedName, signatures.get(-1));
} else {
((CompiledIRMethod) compilable).variable = MethodHandles.publicLookup().findStatic(sourceClass, jittedName, signatures.get(-1));

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

((CompiledIRMethod) compilable).specific = MethodHandles.publicLookup().findStatic(sourceClass, jittedName, entry.getValue());
break;
}
}
} catch(Exception e) {
e.printStackTrace();
}

}


/** Record a begin block. Only eval and script body scopes support this */
public void recordBeginBlock(IRClosure beginBlockClosure) {
throw new RuntimeException("BEGIN blocks cannot be added to: " + this.getClass().getName());
65 changes: 51 additions & 14 deletions core/src/main/java/org/jruby/ir/interpreter/Profiler.java
Original file line number Diff line number Diff line change
@@ -2,10 +2,13 @@

import org.jruby.RubyModule;
import org.jruby.compiler.Compilable;
import org.jruby.internal.runtime.methods.CompiledIRMethod;
import org.jruby.internal.runtime.methods.DynamicMethod;
import org.jruby.ir.Counter;
import org.jruby.ir.IRClosure;
import org.jruby.ir.IRMethod;
import org.jruby.ir.IRScope;
import org.jruby.ir.JIT;
import org.jruby.ir.instructions.CallBase;
import org.jruby.ir.instructions.Instr;
import org.jruby.ir.operands.Operand;
@@ -39,7 +42,7 @@ public class Profiler {
private static final int MAX_NUMBER_OF_INTERESTING_CALLSITES = 10; // We will only look at top n sites

// FIXME: This is a bad statistic by itself. In a super tight loop the hottest site might be >99% of the freq.
private static final float MAX_FREQUENCY = 99.0f; // After we covered this much of the space we do not care
private static final float MAX_FREQUENCY = 100.0f; // After we covered this much of the space we do not care

private static final int MAX_INSTRUCTIONS_TO_INLINE = 500;

@@ -50,7 +53,7 @@ public static class IRCallSite {
long id = INVALID_CALLSITE_ID; // Callsite id (unique to system)
private CallBase call = null; // which instr is at this callsite
long count; // how many times callsite has been executed
Compilable liveMethod; // winner winner chicken dinner we think we have monomorphic target method to inline.
DynamicMethod liveMethod; // winner winner chicken dinner we think we have monomorphic target method to inline.

public IRCallSite() {}

@@ -178,21 +181,35 @@ private static boolean isStillBootstrapping() {
private static long findInliningCandidates(List<IRCallSite> callSites) {
long total = 0; // Total number of calls found in this scope.

//System.out.println("" + callProfile.size() + " candidates.");
// Register all monomorphic found callsites which are eligible for inlining.
for (Long id: callProfile.keySet()) {
CallSiteProfile callSiteProfile = callProfile.get(id);
IRCallSite callSite = callSiteProfile.cs;
boolean monomorphic = callSiteProfile.retallyCallCount();

//System.out.println("candidate: calls=" + callSite.count + ", mono: " + monomorphic + ", KS: " + callSiteProfile.counters.size());
if (monomorphic && !callSite.getCall().inliningBlocked()) {
CallSite runtimeCallSite = callSite.getCall().getCallSite();
if (runtimeCallSite != null && runtimeCallSite instanceof CachingCallSite) {
DynamicMethod method = ((CachingCallSite) runtimeCallSite).getCache().method;

if (!(method instanceof Compilable) || !((Compilable) method).getIRScope().isFullBuildComplete()) continue;
DynamicMethod compilable = callSiteProfile.counters.keySet().iterator().next().getCompilable();

//System.out.println("SCOPE: " + callSiteProfile.counters.keySet().iterator().next());
if (compilable != null) {
callSites.add(callSite);
callSite.liveMethod = (Compilable) method;
callSite.liveMethod = compilable;
} else { // Interpreter
CallSite runtimeCallSite = callSite.getCall().getCallSite();
if (runtimeCallSite != null && runtimeCallSite instanceof CachingCallSite) {
DynamicMethod method = ((CachingCallSite) runtimeCallSite).getCache().method;

if (callSite.getCall().getName().equals("[]")) {
System.out.println(callSite.ic.toStringInstrs());
}
if (!(method instanceof Compilable) || !((Compilable) method).getIRScope().isFullBuildComplete())
continue;

callSites.add(callSite);
callSite.liveMethod = method;
}
}
}

@@ -224,6 +241,7 @@ private static void analyzeProfile() {

if (percentOfTotalCalls < INSIGNIFICANT_PERCENTAGE) break;
freq += percentOfTotalCalls;
//System.out.println("FREQ: " + freq);
if (i++ >= MAX_NUMBER_OF_INTERESTING_CALLSITES || freq > MAX_FREQUENCY) break; // overly simplistic

//System.out.println("Considering: " + ircs.call + " with id: " + ircs.call.callSiteId +
@@ -240,17 +258,30 @@ private static void analyzeProfile() {
// b. use profiled (or last profiled in case more multiple profiled versions)
ic = isClosure ? ic.getScope().getLexicalParent().getFullInterpreterContext() : ic;

Compilable methodToInline = callSite.liveMethod;
DynamicMethod methodToInline = callSite.liveMethod;

if (shouldInline(methodToInline.getIRScope(), callSite.getCall(), ic, isClosure)) {
if (methodToInline instanceof CompiledIRMethod && ic.getScope() instanceof IRMethod) {
((FullInterpreterContext) ic).generateInstructionsForIntepretation();
System.out.println("Inlining " + methodToInline.getName() + " into " + ic.getName());
IRScope scope = ((CompiledIRMethod) methodToInline).getIRMethod();
RubyModule implClass = methodToInline.getImplementationClass();
long start = new java.util.Date().getTime();
ic.getScope().inlineMethod(methodToInline, implClass, implClass.getGeneration(), null, callSite.getCall(), false);//!inlinedScopes.contains(ic));
ic.getScope().inlineMethodJIT((Compilable) methodToInline, implClass, implClass.getGeneration(), null, callSite.getCall(), false);//!inlinedScopes.contains(ic));
inlinedScopes.add(ic);
long end = new java.util.Date().getTime();
System.out.println("Inlined " + methodToInline.getName() + " into " + ic.getName() + " @ instr " + callSite.getCall() +
" in time (ms): " + (end-start) + " # of inlined instrs: " +
methodToInline.getIRScope().getFullInterpreterContext().getInstructions().length);
} else if (methodToInline instanceof Compilable && ic.getScope() instanceof IRMethod) {
System.out.println("Inlining " + methodToInline.getName() + " into " + ic.getName());
IRScope scope = ((Compilable) methodToInline).getIRScope();
if (shouldInline(scope, callSite.getCall(), ic, isClosure)) {
RubyModule implClass = methodToInline.getImplementationClass();
long start = new java.util.Date().getTime();
ic.getScope().inlineMethod((Compilable) methodToInline, implClass, implClass.getGeneration(), null, callSite.getCall(), false);//!inlinedScopes.contains(ic));
inlinedScopes.add(ic);
long end = new java.util.Date().getTime();
System.out.println("Inlined " + methodToInline.getName() + " into " + ic.getName() + " @ instr " + callSite.getCall() +
" in time (ms): " + (end - start) + " # of inlined instrs: " +
scope.getFullInterpreterContext().getInstructions().length);
}
}
}

@@ -289,6 +320,10 @@ public static void initProfiling(IRScope scope) {
//int scopeVersion = scope.getFullInterpreterContext().getVersion();
//if (scopeVersion == UNASSIGNED_VERSION) ic.setVersion(versionCount);

// FIXME: JIT is somehow registering callProfile from original call and registering itself
// as a called scope. Why does only the JIT cause this?
if (scope instanceof IRClosure) return;

// FIXME: I think there is a bug here. If we call IR -> native -> IR then this may still be set and it will
// be inaccurate to record this save callsite info with the currently executing scope.

@@ -309,7 +344,9 @@ public static void initProfiling(IRScope scope) {
}
}

@JIT
public static void markCallAboutToBeCalled(long callsiteId, IRScope scope) {
callerSite.call = null;
callerSite.update(callsiteId, scope.getFullInterpreterContext());
}

2 changes: 2 additions & 0 deletions core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java
Original file line number Diff line number Diff line change
@@ -1299,6 +1299,7 @@ public static void defCompiledInstanceMethod(ThreadContext context, MethodHandle
Visibility newVisibility = Helpers.performNormalMethodChecksAndDetermineVisibility(runtime, clazz, method.getName(), currVisibility);

DynamicMethod newMethod = new CompiledIRMethod(handle, method, newVisibility, clazz, method.receivesKeywordArgs());
method.setCompilable(newMethod);

// FIXME: needs checkID and proper encoding to force hard symbol
Helpers.addInstanceMethod(clazz, method.getName(), newMethod, currVisibility, context, runtime);
@@ -1313,6 +1314,7 @@ public static void defCompiledInstanceMethod(ThreadContext context, MethodHandle
Visibility newVisibility = Helpers.performNormalMethodChecksAndDetermineVisibility(runtime, clazz, method.getName(), currVisibility);

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

// FIXME: needs checkID and proper encoding to force hard symbol
Helpers.addInstanceMethod(clazz, method.getName(), newMethod, currVisibility, context, runtime);
3 changes: 1 addition & 2 deletions core/src/main/java/org/jruby/ir/targets/JVMVisitor.java
Original file line number Diff line number Diff line change
@@ -1297,8 +1297,7 @@ public void MatchInstr(MatchInstr matchInstr) {

@Override
public void ModuleVersionGuardInstr(ModuleVersionGuardInstr moduleversionguardinstr) {
// SSS FIXME: Unused at this time
throw new NotCompilableException("Unsupported instruction: " + moduleversionguardinstr);
// throw new NotCompilableException("Unsupported instruction: " + moduleversionguardinstr);
}

@Override