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

Commits on Jul 1, 2016

  1. Copy the full SHA
    6f52259 View commit details
  2. Copy the full SHA
    e7bb7d6 View commit details

Commits on Jul 7, 2016

  1. Copy the full SHA
    62ca4cf View commit details
  2. twofer 1) kill ancient fixme pointing out we never need to pass in se…

    …lf as it
    
    is implied by the instr itself.  We still need the result to be self for analysis
    but we can eliminate the parameter because it can only be one value ever.
    
    2) cloning during inlining was broken.  If we clone for inlining we need to
    convert to copy(renamed_self, method_receiver).
    enebo committed Jul 7, 2016
    Copy the full SHA
    85f1866 View commit details
1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/compiler/JITCompiler.java
Original file line number Diff line number Diff line change
@@ -246,6 +246,7 @@ public void run() {
}
}

if (Options.IR_PROFILE.load()) method.getIRScope().setCompilable(method);
String key = SexpMaker.sha1(method.getIRScope());
JVMVisitor visitor = new JVMVisitor();
MethodJITClassGenerator generator = new MethodJITClassGenerator(className, methodName, key, runtime, method, visitor);
1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/ir/Compiler.java
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import org.jruby.util.cli.Options;

public class Compiler extends IRTranslator<ScriptAndCode, ClassDefiningClassLoader> {

4 changes: 1 addition & 3 deletions core/src/main/java/org/jruby/ir/IRManager.java
Original file line number Diff line number Diff line change
@@ -2,7 +2,6 @@

import java.util.EnumSet;
import org.jruby.RubyInstanceConfig;
import org.jruby.ir.instructions.Instr;
import org.jruby.ir.instructions.LineNumberInstr;
import org.jruby.ir.instructions.ReceiveSelfInstr;
import org.jruby.ir.instructions.ToggleBacktraceInstr;
@@ -20,7 +19,6 @@
import java.util.List;
import java.util.Set;
import org.jruby.ir.passes.DeadCodeElimination;
import org.jruby.ir.passes.LocalOptimizationPass;
import org.jruby.ir.passes.OptimizeDelegationPass;
import org.jruby.ir.passes.OptimizeDynScopesPass;

@@ -205,7 +203,7 @@ public LineNumberInstr newLineNumber(int line) {

}

private ReceiveSelfInstr receiveSelfInstr = new ReceiveSelfInstr(Self.SELF);
private ReceiveSelfInstr receiveSelfInstr = new ReceiveSelfInstr();

public ReceiveSelfInstr getReceiveSelfInstr() {
return receiveSelfInstr;
15 changes: 8 additions & 7 deletions core/src/main/java/org/jruby/ir/IRScope.java
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@
import org.jruby.compiler.Compilable;
import org.jruby.compiler.JITCompiler.MethodJITClassGenerator;
import org.jruby.internal.runtime.methods.CompiledIRMethod;
import org.jruby.internal.runtime.methods.MixedModeIRMethod;
import org.jruby.ir.dataflow.analyses.LiveVariablesProblem;
import org.jruby.ir.dataflow.analyses.StoreLocalVarPlacementProblem;
import org.jruby.ir.dataflow.analyses.UnboxableOpsAnalysisProblem;
@@ -1056,10 +1057,11 @@ public void resetState() {
}

private FullInterpreterContext inlineMethodCommon(Compilable method, RubyModule implClass, int classToken, BasicBlock basicBlock, CallBase call, boolean cloneHost) {
alreadyHasInline = true;
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();
FullInterpreterContext newContext = getFullInterpreterContext().duplicate();

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

@@ -1091,17 +1093,16 @@ public void inlineMethodJIT(Compilable method, RubyModule implClass, int classTo
FullInterpreterContext newContext = inlineMethodCommon(method, implClass, classToken, basicBlock, call, cloneHost);

// We are not running any JIT-specific passes here.
Ruby runtime = implClass.getRuntime();

newContext.linearizeBasicBlocks();
this.fullInterpreterContext = newContext;

/*
System.out.println("DONE!");
System.out.println("cfg :" + getCFG().toStringGraph());
System.out.println("instrs:" + getCFG().toStringInstrs());
*/
if (compilable instanceof MixedModeIRMethod) {
runtime.getJITCompiler().getTaskFor(runtime.getCurrentContext(), compilable).run();
return;
}

Ruby runtime = implClass.getRuntime();
String key = SexpMaker.sha1(this);
JVMVisitor visitor = new JVMVisitor();
MethodJITClassGenerator generator = new MethodJITClassGenerator(implClass.getName(), getName(), key, runtime, compilable, visitor);
3 changes: 2 additions & 1 deletion core/src/main/java/org/jruby/ir/instructions/CallBase.java
Original file line number Diff line number Diff line change
@@ -400,7 +400,8 @@ public boolean targetRequiresCallersFrame() {

@Override
public String[] toStringNonOperandArgs() {
return new String[] { "n:" + getName(), "t:" + callType.toString().substring(0, 2), "cl:"+ hasClosure};
return new String[] { "n:" + getName(), "t:" + callType.toString().substring(0, 2),
"cl:" + hasClosure, "id: " + siteId};
}

public static boolean containsArgSplat(Operand[] arguments) {
Original file line number Diff line number Diff line change
@@ -2,16 +2,16 @@

import org.jruby.ir.IRVisitor;
import org.jruby.ir.Operation;
import org.jruby.ir.operands.Variable;
import org.jruby.ir.operands.Self;
import org.jruby.ir.persistence.IRReaderDecoder;
import org.jruby.ir.persistence.IRWriterEncoder;
import org.jruby.ir.transformations.inlining.CloneInfo;
import org.jruby.ir.transformations.inlining.SimpleCloneInfo;


public class ReceiveSelfInstr extends NoOperandResultBaseInstr implements FixedArityInstr {
// SSS FIXME: destination always has to be a local variable '%self'. So, is this a redundant arg?
public ReceiveSelfInstr(Variable result) {
super(Operation.RECV_SELF, result);
public ReceiveSelfInstr() {
super(Operation.RECV_SELF, Self.SELF);

assert result != null: "ReceiveSelfInstr result is null";
}
@@ -20,9 +20,8 @@ public ReceiveSelfInstr(Variable result) {
public Instr clone(CloneInfo info) {
if (info instanceof SimpleCloneInfo) return this;

// receive-self will disappear after inlining and all uses of %self will be replaced by the call receiver
// FIXME: What about 'self' in closures??
return null;
// inlining - we convert to renamed variable which replaces %self and give it the calls receiver.
return new CopyInstr(info.getRenamedVariable(result), info.getRenamedSelfVariable(null));
}

@Override
99 changes: 75 additions & 24 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.AbstractIRMethod;
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.Counter;
import org.jruby.ir.IRClosure;
import org.jruby.ir.IREvalScript;
import org.jruby.ir.IRMethod;
import org.jruby.ir.IRScope;
import org.jruby.ir.JIT;
@@ -18,6 +21,9 @@
import org.jruby.runtime.callsite.CachingCallSite;

import java.util.*;
import org.jruby.util.cli.Options;
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;

/**
* Definitions in profiler:
@@ -34,6 +40,8 @@
* might inline some of those and then it will flush collected data and start over.
*/
public class Profiler {
private static final Logger LOG = LoggerFactory.getLogger(Profiler.class);

public static final long INVALID_CALLSITE_ID = -1;
public static final int UNASSIGNED_VERSION = -1;
private static final int PROFILE_PERIOD = 20000;
@@ -65,7 +73,7 @@ public IRCallSite(IRCallSite cs) {
}

/**
* Interpreter trivially knows what the call is but the JIT only knows the callsiteId.
* Interpreter saves call but JIT only has the callsiteId and must find it based on that id.
*/
public CallBase getCall() {
if (call == null) call = findCall();
@@ -181,33 +189,57 @@ 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.");
if (Options.IR_PROFILE_DEBUG.load()) LOG.info("begin findInliningCandidate {} candidates to examine.", callProfile.size());

// 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();
CallBase call = callSite.getCall();

//System.out.println("candidate: calls=" + callSite.count + ", mono: " + monomorphic + ", KS: " + callSiteProfile.counters.size());
if (Options.IR_PROFILE_DEBUG.load()) {
LOG.info("id: {}, # of calls: {}, # of targets: {}", callSite.id, callSite.count, callSiteProfile.counters.size());
}

// FIXME: Why is call sometimes null?
if (monomorphic && call != null && !call.inliningBlocked() && !callSite.scope.inliningAllowed()) {
Compilable compilable = callSiteProfile.counters.keySet().iterator().next().getCompilable();

//System.out.println("SCOPE: " + callSiteProfile.counters.keySet().iterator().next());
if (compilable != null && compilable instanceof CompiledIRMethod) {
if (callSite.scope.getCompilable() == null) continue;
IRScope inlineScope = callSiteProfile.counters.keySet().iterator().next();

if (Options.IR_PROFILE_DEBUG.load()) LOG.info(" mono scope: {}", inlineScope);

Compilable compilable = inlineScope.getCompilable();
if (compilable != null && compilable instanceof CompiledIRMethod || compilable instanceof MixedModeIRMethod) {
if (callSite.scope.getCompilable() == null) {
if (Options.IR_PROFILE_DEBUG.load()) {
if (callSite.scope instanceof IREvalScript) {
LOG.info(" rejected: scope in script body [JITTED]");
} else {
LOG.info(" rejected: no parent compilable found [JITTED]");
}
}
continue;
}

if (Options.IR_PROFILE_DEBUG.load()) LOG.info(" aceepted [JITTED] {}", call);
callSites.add(callSite);
callSite.liveMethod = compilable;
} else { // Interpreter
CallSite runtimeCallSite = callSite.getCall().getCallSite();
if (runtimeCallSite != null && runtimeCallSite instanceof CachingCallSite) {
DynamicMethod method = ((CachingCallSite) runtimeCallSite).getCache().method;

if (!(method instanceof Compilable)) continue;
if (!areScopesFullyBuilt(callSite.scope, (Compilable) method)) continue;
if (!(method instanceof Compilable)) {
if (Options.IR_PROFILE_DEBUG.load()) LOG.info(" rejected: no parent compilable found [INTERPD]");
continue;
}

if (!areScopesFullyBuilt(callSite.scope, (Compilable) method)) {
if (Options.IR_PROFILE_DEBUG.load()) LOG.info(" rejected: still startup scope [INTERPD]");
continue;
}

if (Options.IR_PROFILE_DEBUG.load()) LOG.info(" aceepted [INTERPD]");
callSites.add(callSite);
callSite.liveMethod = (Compilable) method;
}
@@ -217,6 +249,8 @@ private static long findInliningCandidates(List<IRCallSite> callSites) {
total += callSite.count;
}

if (Options.IR_PROFILE_DEBUG.load()) LOG.info("end findInliningCandidates: {} total calls found processed", total);

return total;
}

@@ -235,7 +269,7 @@ private static void analyzeProfile() {
long total = findInliningCandidates(callSites);
Collections.sort(callSites, callSiteComparator);

//System.out.println("Total calls this period: " + total + ", candidate callsites: " + callSites.size());
if (Options.IR_PROFILE_DEBUG.load()) LOG.info("begin analyzeProfile: # of calls {} of callsites: {}", total, callSites.size());

// Find top N call sites
double freq = 0.0;
@@ -246,12 +280,24 @@ 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.getSiteId() +
//" in scope " + ircs.ic.getScope() + " with count " + ircs.count + "; contrib " + contrib + "; freq: " + freq);
if (Options.IR_PROFILE_DEBUG.load()) LOG.info(" call: {}, % of calls: {}", callSite.call, percentOfTotalCalls);

if (i++ >= MAX_NUMBER_OF_INTERESTING_CALLSITES) {
if (Options.IR_PROFILE_DEBUG.load()) LOG.info(" rejected: too many callsite examined {}", i);
break;
}
if (freq > MAX_FREQUENCY) {
if (Options.IR_PROFILE_DEBUG.load()) LOG.info(" rejected: too high a freq {}", freq);
break;
}


//System.out.println("Considering: " + callSite.call + " with id: " + callSite.call.getSiteId() +
//" in scope " + callSite.scope + " with count " + callSite.count + "; % of calls: " + percentOfTotalCalls +
// "; freq: " + freq);

if (callSite.scope.getFullInterpreterContext() == null) continue;

InterpreterContext parentIC = callSite.scope.getFullInterpreterContext();
boolean isClosure = parentIC.getScope() instanceof IRClosure;
@@ -264,13 +310,16 @@ private static void analyzeProfile() {
parentIC = isClosure ? parentIC.getScope().getLexicalParent().getFullInterpreterContext() : parentIC;

// For now we are not inlining into anything but a method
if (parentIC == null || !(parentIC.getScope() instanceof IRMethod)) continue;
if (parentIC == null || !(parentIC.getScope() instanceof IRMethod)) {
if (Options.IR_PROFILE_DEBUG.load()) LOG.info(" rejected: can only inline into a method");
continue;
}

Compilable methodToInline = callSite.liveMethod;

if (methodToInline instanceof CompiledIRMethod) {
System.out.println("Inlining " + methodToInline.getName() + " into " + parentIC.getName());
IRScope scope = ((CompiledIRMethod) methodToInline).getIRScope();
if (methodToInline instanceof CompiledIRMethod || methodToInline instanceof MixedModeIRMethod) {
if (Options.IR_PROFILE_DEBUG.load()) LOG.info("Inlining " + methodToInline.getName() + " into " + parentIC.getName());
IRScope scope = ((AbstractIRMethod) methodToInline).getIRScope();
CallBase call;
// If we are in same batch of interesting callsites then the underlying scope has already
// cloned any other interesting callsites.
@@ -284,7 +333,7 @@ private static void analyzeProfile() {
parentIC.getScope().inlineMethodJIT(methodToInline, implClass, implClass.getGeneration(), null, call, false);//!inlinedScopes.contains(ic));
inlinedScopes.add(parentIC.getScope());
long end = new java.util.Date().getTime();
System.out.println("Inlined " + methodToInline.getName() + " into " + parentIC.getName() + " @ instr " + callSite.getCall() +
if (Options.IR_PROFILE_DEBUG.load()) LOG.info("Inlined " + methodToInline.getName() + " into " + parentIC.getName() + " @ instr " + callSite.getCall() +
" in time (ms): " + (end - start));

} else if (methodToInline instanceof Compilable) {
@@ -295,7 +344,7 @@ private static void analyzeProfile() {
parentIC.getScope().inlineMethod(methodToInline, implClass, implClass.getGeneration(), null, callSite.getCall(), false);//!inlinedScopes.contains(ic));
inlinedScopes.add(parentIC.getScope());
long end = new java.util.Date().getTime();
System.out.println("Inlined " + methodToInline.getName() + " into " + parentIC.getName() + " @ instr " + callSite.getCall() +
if (Options.IR_PROFILE_DEBUG.load()) LOG.info("Inlined " + methodToInline.getName() + " into " + parentIC.getName() + " @ instr " + callSite.getCall() +
" in time (ms): " + (end - start) + " # of inlined instrs: " +
scope.getFullInterpreterContext().getInstructions().length);
}
@@ -312,22 +361,24 @@ private static void analyzeProfile() {

// Every 1M thread polls, discard stats
if (globalClockCount % 1000000 == 0) globalClockCount = 0;

if (Options.IR_PROFILE_DEBUG.load()) LOG.info("end analyzCallsites");
}

/**
* All methods will inline so long as they have been fully built. A hot closure will inline through the method
* which call it.
*/
private static boolean shouldInline(IRScope scopeToInline, CallBase call, InterpreterContext ic, boolean isClosure) {
private static boolean shouldInline(IRScope scopeToInline, CallBase call, InterpreterContext parentIC, boolean isClosure) {
Instr[] instrs = scopeToInline.getFullInterpreterContext().getInstructions();
if (instrs == null || instrs.length > MAX_INSTRUCTIONS_TO_INLINE) return false;

// FIXME: Closure getting lexical parent can end up with null. We should fix that in parent method to remove this null check.
boolean fullBuild = ic != null && ic.getScope().isFullBuildComplete();
boolean fullBuild = parentIC != null && parentIC.getScope().isFullBuildComplete();

if (isClosure) {
Operand closureArg = call.getClosureArg(null);
return fullBuild && closureArg instanceof WrappedIRClosure && ((WrappedIRClosure) closureArg).getClosure() == ic.getScope();
return fullBuild && closureArg instanceof WrappedIRClosure && ((WrappedIRClosure) closureArg).getClosure() == parentIC.getScope();
}

return fullBuild;
Original file line number Diff line number Diff line change
@@ -105,7 +105,6 @@ public IRubyObject interpret(ThreadContext context, Block block, IRubyObject sel
}
break;
case MOD_OP:
if (profile) Profiler.modificationTick();
setResult(temp, currDynScope, instr, instr.interpret(context, currScope, currDynScope, self, temp));
break;
case OTHER_OP:
Original file line number Diff line number Diff line change
@@ -66,7 +66,7 @@ public Label getRenamedLabel(Label label) {
* @param self to be renamed
* @return the new self or itself
*/
protected abstract Variable getRenamedSelfVariable(Variable self);
public abstract Variable getRenamedSelfVariable(Variable self);

/**
* How are typical variables renamed if they were not yet found in the variable renaming map?
1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/util/cli/Options.java
Original file line number Diff line number Diff line change
@@ -115,6 +115,7 @@ public class Options {

public static final Option<Boolean> IR_DEBUG = bool(IR, "ir.debug", false, "Debug generation of JRuby IR.");
public static final Option<Boolean> IR_PROFILE = bool(IR, "ir.profile", false, "[EXPT]: Profile IR code during interpretation.");
public static final Option<Boolean> IR_PROFILE_DEBUG = bool(IR, "ir.profile.debug", false, "[EXPT]: Dump profiling debug info if profiling.");
public static final Option<Boolean> IR_COMPILER_DEBUG = bool(IR, "ir.compiler.debug", false, "Debug compilation of JRuby IR.");
public static final Option<Boolean> IR_VISUALIZER = bool(IR, "ir.visualizer", false, "Visualization of JRuby IR.");
public static final Option<Boolean> IR_UNBOXING = bool(IR, "ir.unboxing", false, "Implement unboxing opts.");
Loading