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

Commits on Dec 19, 2015

  1. New Site interface for getting (and for now setting) siteId. All calls

    and yield currently use this id but this could be expanded for any types
    of sites we want to keep track of.
    
    Using new Site we stop using ipc for find a call/yield instr which should
    mean we do not need to generateInstructions in order to perform an inline
    any more.
    
    The main problem remaining with siteId is it is hard-wired into the
    constructor to get a new one every time an object is created.  So we have
    a setSiteId() method to restore the siteId on a cloned instance.  To fix this
    we should make a clone constructor for all callbase and yield instrs.  This
    is a bit painful so I am only mentioning it should be done at some point.
    enebo committed Dec 19, 2015
    Copy the full SHA
    3619ab1 View commit details
  2. Copy the full SHA
    7f255e9 View commit details
  3. Start to clean up

    enebo committed Dec 19, 2015
    Copy the full SHA
    f660230 View commit details

Commits on Dec 20, 2015

  1. CFGInliner was missing some cloneForInlining so original closure BBs …

    …were
    
    getting put into destination CFGs rescueMap.
    
    Also added some more debugging hidden by debug field in CFGInliner.
    enebo committed Dec 20, 2015
    Copy the full SHA
    3d1b500 View commit details

Commits on Dec 23, 2015

  1. Speculative fix to crash for inlining:

      def self.new(*)
        super
      end
    
    This introduces a problem with ScopeModule not really having a scope to
    figure out what its module is anymore (the scope was inlined away).  The
    answer is to use the inlining candidate type and for non-zero ScopeModules it
    is to subtract one from scopeDepth? (I am less sure about this).  With this
    change, self.new still does not properly inline but this seems like a step
    in the right direction (and I want @subbuss to see the approach).
    enebo committed Dec 23, 2015
    5
    Copy the full SHA
    61f8da4 View commit details
4 changes: 2 additions & 2 deletions core/src/main/java/org/jruby/compiler/JITCompiler.java
Original file line number Diff line number Diff line change
@@ -441,8 +441,8 @@ public void compile(JVMVisitorMethodContext context) {
throw new NotCompilableException("Could not compile " + method + "; instruction count " + insnCount + " exceeds threshold of " + Options.JIT_MAXSIZE.load());
}

method.getIRScope().setCompilable(method);
System.out.println("Adding compiable to " + method.getIRScope());
if (Options.IR_PROFILE.load()) method.getIRScope().setCompilable(method);

// 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);
34 changes: 11 additions & 23 deletions core/src/main/java/org/jruby/ir/IRScope.java
Original file line number Diff line number Diff line change
@@ -1028,8 +1028,7 @@ public void resetState() {
}
}

// FIXME: Passing in DynamicMethod is gross here we probably can minimally cast to Compilable
public void inlineMethod(Compilable method, RubyModule implClass, int classToken, BasicBlock basicBlock, CallBase call, boolean cloneHost) {
private FullInterpreterContext inlineMethodCommon(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.
@@ -1045,41 +1044,30 @@ public void inlineMethod(Compilable method, RubyModule implClass, int classToken
pass.run(this);
}

return newContext;
}

// FIXME: Passing in DynamicMethod is gross here we probably can minimally cast to Compilable
public void inlineMethod(Compilable method, RubyModule implClass, int classToken, BasicBlock basicBlock, CallBase call, boolean cloneHost) {
FullInterpreterContext newContext = inlineMethodCommon(method, implClass, classToken, basicBlock, call, cloneHost);

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

System.out.println(fullInterpreterContext.toStringInstrs());
compilable.setInterpreterContext(fullInterpreterContext);
alreadyHasInline = true;
// 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();
FullInterpreterContext newContext = inlineMethodCommon(method, implClass, classToken, basicBlock, call, cloneHost);

new CFGInliner(newContext).inlineMethod(methodToInline, implClass, classToken, basicBlock, call, cloneHost);
// We are not running any JIT-specific passes here.

// Reset state
resetState();

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


//runCompilerPasses(getManager().getJITPasses(this));
newContext.generateInstructionsForIntepretation();
newContext.linearizeBasicBlocks();
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();
16 changes: 12 additions & 4 deletions core/src/main/java/org/jruby/ir/instructions/CallBase.java
Original file line number Diff line number Diff line change
@@ -19,10 +19,10 @@

import static org.jruby.ir.IRFlags.*;

public abstract class CallBase extends NOperandInstr implements ClosureAcceptingInstr {
private static long callSiteCounter = 1;
public abstract class CallBase extends NOperandInstr implements ClosureAcceptingInstr, Site {
static long callSiteCounter = 1;

public long callSiteId;
private long siteId;
private final CallType callType;
protected String name;
protected CallSite callSite;
@@ -42,7 +42,7 @@ protected CallBase(Operation op, CallType callType, String name, Operand receive
boolean potentiallyRefined) {
super(op, getOperands(receiver, args, closure));

this.callSiteId = callSiteCounter++;
siteId = callSiteCounter++;
argsCount = args.length;
hasClosure = closure != null;
this.name = name;
@@ -128,6 +128,14 @@ public CallSite getCallSite() {
return callSite;
}

public long getSiteId() {
return siteId;
}

public void setSiteId(long siteId) {
this.siteId = siteId;
}

public void setCallSite(CallSite callSite) {
this.callSite = callSite;
}
10 changes: 10 additions & 0 deletions core/src/main/java/org/jruby/ir/instructions/Site.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.jruby.ir.instructions;

/**
* Represents a site for a call or yield. Possibly other types of sites.
*/
public interface Site {
public long getSiteId();
// FIXME: It would be nice to eliminate needing to change siteid via cloning.
public void setSiteId(long siteId);
}
12 changes: 11 additions & 1 deletion core/src/main/java/org/jruby/ir/instructions/YieldInstr.java
Original file line number Diff line number Diff line change
@@ -15,15 +15,17 @@
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;

public class YieldInstr extends TwoOperandResultBaseInstr implements FixedArityInstr {
public class YieldInstr extends TwoOperandResultBaseInstr implements FixedArityInstr, Site {
public final boolean unwrapArray;
public long siteId;

public YieldInstr(Variable result, Operand block, Operand arg, boolean unwrapArray) {
super(Operation.YIELD, result, block, arg == null ? UndefinedValue.UNDEFINED : arg);

assert result != null: "YieldInstr result is null";

this.unwrapArray = unwrapArray;
this.siteId = CallBase.callSiteCounter++;
}

public Operand getBlockArg() {
@@ -34,6 +36,14 @@ public Operand getYieldArg() {
return getOperand2();
}

public long getSiteId() {
return siteId;
}

public void setSiteId(long siteId) {
this.siteId = siteId;
}

@Override
public Instr clone(CloneInfo ii) {
// FIXME: Is it necessary to clone a yield instruction in a method
14 changes: 8 additions & 6 deletions core/src/main/java/org/jruby/ir/interpreter/Profiler.java
Original file line number Diff line number Diff line change
@@ -76,15 +76,15 @@ private CallBase findCall() {
FullInterpreterContext fic = scope.getFullInterpreterContext();
for (BasicBlock bb: fic.getLinearizedBBList()) {
for (Instr instr: bb.getInstrs()) {
if (instr instanceof CallBase && ((CallBase) instr).callSiteId == id) return (CallBase) instr;
if (instr instanceof CallBase && ((CallBase) instr).getSiteId() == id) return (CallBase) instr;
}
}

return null;
}

public int hashCode() {
return (int) call.callSiteId;
return (int) call.getSiteId();
}

public void update(long callSiteId, IRScope scope) {
@@ -94,7 +94,7 @@ public void update(long callSiteId, IRScope scope) {

public void update(CallBase call, IRScope scope) {
this.scope = scope;
this.id = call.callSiteId;
this.id = call.getSiteId();
this.call = call;
}
}
@@ -249,7 +249,7 @@ private static void analyzeProfile() {
//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 +
//System.out.println("Considering: " + ircs.call + " with id: " + ircs.call.getSiteId() +
//" in scope " + ircs.ic.getScope() + " with count " + ircs.count + "; contrib " + contrib + "; freq: " + freq);


@@ -269,8 +269,7 @@ private static void analyzeProfile() {
Compilable methodToInline = callSite.liveMethod;

if (methodToInline instanceof CompiledIRMethod) {
((FullInterpreterContext) parentIC).generateInstructionsForIntepretation();
System.out.println("Inlined " + methodToInline.getName() + " into " + parentIC.getName());
System.out.println("Inlining " + methodToInline.getName() + " into " + parentIC.getName());
IRScope scope = ((CompiledIRMethod) methodToInline).getIRMethod();
CallBase call;
// If we are in same batch of interesting callsites then the underlying scope has already
@@ -285,6 +284,9 @@ 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() +
" in time (ms): " + (end - start));

} else if (methodToInline instanceof Compilable) {
IRScope scope = (methodToInline).getIRScope();
if (shouldInline(scope, callSite.getCall(), parentIC, isClosure)) {
8 changes: 8 additions & 0 deletions core/src/main/java/org/jruby/ir/operands/ScopeModule.java
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
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.InlineCloneInfo;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.DynamicScope;
import org.jruby.runtime.Helpers;
@@ -45,6 +46,13 @@ public void addUsedVariables(List<Variable> l) {

@Override
public Operand cloneForInlining(CloneInfo ii) {
if (ii instanceof InlineCloneInfo) {
if (scopeModuleDepth == 0) { // replace scope with inlined candidate receiver
return ((InlineCloneInfo) ii).getRenamedSelfVariable(null);
} else { // otherwise we only lost one scope from inlining...subtract one.
return ModuleFor(scopeModuleDepth - 1);
}
}
return this;
}

19 changes: 10 additions & 9 deletions core/src/main/java/org/jruby/ir/representations/BasicBlock.java
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
import org.jruby.ir.IRManager;
import org.jruby.ir.instructions.CallBase;
import org.jruby.ir.instructions.Instr;
import org.jruby.ir.instructions.Site;
import org.jruby.ir.instructions.YieldInstr;
import org.jruby.ir.listeners.InstructionsListener;
import org.jruby.ir.listeners.InstructionsListenerDecorator;
@@ -110,17 +111,17 @@ public boolean isEmpty() {

// Adds all instrs after the found instr to a new BB and removes them from the original BB
// If includeSpltpointInstr is true it will include that instr in the new BB.
public BasicBlock splitAtInstruction(Instr splitPoint, Label newLabel, boolean includeSplitPointInstr) {
public BasicBlock splitAtInstruction(Site splitPoint, Label newLabel, boolean includeSplitPointInstr) {
BasicBlock newBB = new BasicBlock(cfg, newLabel);
int idx = 0;
int numInstrs = instrs.size();
boolean found = false;
for (Instr i: instrs) {
if (i.getIPC() == splitPoint.getIPC()) found = true;
if (i instanceof Site && ((Site) i).getSiteId() == splitPoint.getSiteId()) found = true;

// Move instructions from split point into the new bb
if (found) {
if (includeSplitPointInstr || i.getIPC() != splitPoint.getIPC()) newBB.addInstr(i);
if (includeSplitPointInstr || !(i instanceof Site) || ((Site) i).getSiteId() != splitPoint.getSiteId()) newBB.addInstr(i);
} else {
idx++;
}
@@ -147,10 +148,10 @@ public BasicBlock clone(CloneInfo info, CFG newCFG) {
Instr newInstr = instr.clone(info);
// Inlining clones the original CFG/BBs and we want to maintain ipc since it is how
// we find which instr we want (we clone original instr and ipc is our identity).
if (info instanceof SimpleCloneInfo && ((SimpleCloneInfo) info).shouldCloneIPC()) {
newInstr.setIPC(instr.getIPC());
newInstr.setRPC(instr.getRPC());
}
//if (info instanceof SimpleCloneInfo && ((SimpleCloneInfo) info).shouldCloneIPC()) {
// newInstr.setIPC(instr.getIPC());
// newInstr.setRPC(instr.getRPC());
//}

// All call-derived types do not clone this field. Inliner clones original instrs
// and we need this preserved to make sure we do not endless inline the same call.
@@ -162,8 +163,8 @@ public BasicBlock clone(CloneInfo info, CFG newCFG) {
// If an inline occurs and profiler decides before new inlined host scope has come into
// play it will not be able to find the current callsite. By keeping the same values
// the profiler will continue to work.
if (instr instanceof CallBase) {
((CallBase) newInstr).callSiteId = ((CallBase) instr).callSiteId;
if (instr instanceof Site) {
((Site) newInstr).setSiteId(((Site) instr).getSiteId());
}

if (newInstr != null) { // inliner may kill off unneeded instr
1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/ir/representations/CFG.java
Original file line number Diff line number Diff line change
@@ -203,6 +203,7 @@ public void addGlobalEnsureBB(BasicBlock geb) {
}

public void setRescuerBB(BasicBlock block, BasicBlock rescuerBlock) {
if (block == null) new Exception().printStackTrace();
rescuerMap.put(block, rescuerBlock);
}

14 changes: 5 additions & 9 deletions core/src/main/java/org/jruby/ir/targets/JVMVisitor.java
Original file line number Diff line number Diff line change
@@ -73,7 +73,7 @@ public class JVMVisitor extends IRVisitor {
public JVMVisitor() {
this.jvm = Options.COMPILE_INVOKEDYNAMIC.load() ? new JVM7() : new JVM6();
this.methodIndex = 0;
this.scopeMap = new HashMap();
this.scopeMap = new HashMap<String, IRScope>();
}

public Class compile(IRScope scope, ClassDefiningClassLoader jrubyClassLoader) {
@@ -776,17 +776,14 @@ public void BuildCompoundStringInstr(BuildCompoundStringInstr compoundstring) {
@Override
public void BuildDynRegExpInstr(BuildDynRegExpInstr instr) {
final IRBytecodeAdapter m = jvmMethod();
SkinnyMethodAdapter a = m.adapter;

RegexpOptions options = instr.getOptions();
final Operand[] operands = instr.getPieces();

Runnable r = new Runnable() {
@Override
public void run() {
m.loadContext();
for (int i = 0; i < operands.length; i++) {
Operand operand = operands[i];
for (Operand operand: operands) {
visit(operand);
}
}
@@ -833,7 +830,7 @@ public void CallInstr(CallInstr callInstr) {
Variable result = callInstr.getResult();

if (Options.IR_PROFILE.load()) {
jvmAdapter().ldc(callInstr.callSiteId);
jvmAdapter().ldc(callInstr.getSiteId());
jvmAdapter().getstatic(jvm.clsData().clsName, jvm.clsData().methodData().method.adapter.getName() + "_IRScope", ci(IRScope.class));
jvmAdapter().invokestatic(p(Profiler.class), "markCallAboutToBeCalled", sig(void.class, long.class, IRScope.class));
}
@@ -1187,8 +1184,7 @@ private void superCommon(String name, CallInstr instr, Operand[] args, Operand d
jvmAdapter().checkcast(p(RubyClass.class));

// process args
for (int i = 0; i < args.length; i++) {
Operand operand = args[i];
for (Operand operand: args) {
visit(operand);
}

@@ -1430,7 +1426,7 @@ public void PrepareBlockArgsInstr(PrepareBlockArgsInstr instr) {
jvmMethod().loadContext();
jvmMethod().loadSelfBlock();
jvmMethod().loadArgs();
jvmAdapter().ldc(((IRClosure)jvm.methodData().scope).receivesKeywordArgs());
jvmAdapter().ldc(jvm.methodData().scope.receivesKeywordArgs());
jvmMethod().invokeIRHelper("prepareBlockArgs", sig(IRubyObject[].class, ThreadContext.class, Block.class, IRubyObject[].class, boolean.class));
jvmMethod().storeArgs();
}
Original file line number Diff line number Diff line change
@@ -68,24 +68,25 @@ private Variable getReceiverVariable(Operand receiver) {
}

private BasicBlock findCallsiteBB(CallBase call) {
System.out.println("LOOKING FOR IPC: " + call.getIPC());
long callSiteId = call.getSiteId();
if (debug) System.out.println("LOOKING FOR CALLSITEID: " + callSiteId);
for (BasicBlock bb: cfg.getBasicBlocks()) {
for (Instr i: bb.getInstrs()) {
System.out.println("IPC " + i.getIPC() + " = " + i);
// Some instrs reuse instrs (like LineNumberInstr) so we need to add call check.
if (i.getIPC() == call.getIPC() && i instanceof CallBase) {
System.out.println("Found it!!!! -- " + call + ", i: " + i + ", IPC: " + call.getIPC());
if (i instanceof CallBase && ((CallBase) i).getSiteId() == callSiteId) {
if (debug) System.out.println("Found it!!!! -- " + call + ", i: " + i);
return bb;
}
}
}

if (debug) System.out.println("Did not find it");
return null;
}

private void printInlineDebugPrologue(IRScope methodScope, CallBase call) {
System.out.println("---------------------------------- PROLOGUE (start) --------");
System.out.println("Looking for: " + call.getIPC() + ": " + call);
System.out.println("Looking for: " + call.getSiteId() + ": " + call);
printInlineCFG(cfg);
System.out.println("source cfg :" + methodScope.getCFG().toStringGraph());
System.out.println("source instrs:" + methodScope.getCFG().toStringInstrs());
@@ -388,9 +389,9 @@ private void inlineClosureAtYieldSite(InlineCloneInfo ii, IRClosure cl, BasicBlo

BasicBlock cbProtector = ii.getRenamedBB(closureCFG.getRescuerBBFor(cb));
if (cbProtector != null) {
cfg.setRescuerBB(cb, cbProtector);
cfg.setRescuerBB(cb.cloneForInlining(ii), cbProtector);
} else if (yieldBBrescuer != null) {
cfg.setRescuerBB(cb, yieldBBrescuer);
cfg.setRescuerBB(cb.cloneForInlining(ii), yieldBBrescuer);
}
}
}
Original file line number Diff line number Diff line change
@@ -150,7 +150,7 @@ protected Label getRenamedLabelSimple(Label l) {
return getHostScope().getNewLabel();
}

protected Variable getRenamedSelfVariable(Variable self) {
public Variable getRenamedSelfVariable(Variable self) {
return callReceiver;
}