IRManager.java
package org.jruby.ir;
import org.jruby.RubyInstanceConfig;
import org.jruby.ir.listeners.IRScopeListener;
import org.jruby.ir.listeners.InstructionsListener;
import org.jruby.ir.operands.Nil;
import org.jruby.ir.operands.TemporaryLocalVariable;
import org.jruby.ir.passes.BasicCompilerPassListener;
import org.jruby.ir.passes.CompilerPass;
import org.jruby.ir.passes.CompilerPassListener;
import org.jruby.ir.passes.CompilerPassScheduler;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
public class IRManager {
public static final String SAFE_COMPILER_PASSES = "";
public static final String DEFAULT_COMPILER_PASSES = "OptimizeTempVarsPass,LocalOptimizationPass";
public static final String DEFAULT_JIT_PASSES = "DeadCodeElimination,AddLocalVarLoadStoreInstructions,OptimizeDynScopesPass,AddCallProtocolInstructions,EnsureTempsAssigned";
public static final String DEFAULT_INLINING_COMPILER_PASSES = "LocalOptimizationPass";
private int dummyMetaClassCount = 0;
private final IRModuleBody object = new IRClassBody(this, null, "Object", "", 0, null);
private final Nil nil = new Nil();
private final org.jruby.ir.operands.Boolean trueObject = new org.jruby.ir.operands.Boolean(true);
private final org.jruby.ir.operands.Boolean falseObject = new org.jruby.ir.operands.Boolean(false);
// Listeners for debugging and testing of IR
private Set<CompilerPassListener> passListeners = new HashSet<CompilerPassListener>();
private CompilerPassListener defaultListener = new BasicCompilerPassListener();
private InstructionsListener instrsListener = null;
private IRScopeListener irScopeListener = null;
// FIXME: Eventually make these attrs into either a) set b) part of state machine
private List<CompilerPass> compilerPasses;
private List<CompilerPass> inliningCompilerPasses;
private List<CompilerPass> jitPasses;
private List<CompilerPass> safePasses;
// If true then code will not execute (see ir/ast tool)
private boolean dryRun = false;
public IRManager() {
compilerPasses = CompilerPass.getPassesFromString(RubyInstanceConfig.IR_COMPILER_PASSES, DEFAULT_COMPILER_PASSES);
inliningCompilerPasses = CompilerPass.getPassesFromString(RubyInstanceConfig.IR_COMPILER_PASSES, DEFAULT_INLINING_COMPILER_PASSES);
jitPasses = CompilerPass.getPassesFromString(RubyInstanceConfig.IR_JIT_PASSES, DEFAULT_JIT_PASSES);
safePasses = CompilerPass.getPassesFromString(null, SAFE_COMPILER_PASSES);
}
public boolean isDryRun() {
return dryRun;
}
public void setDryRun(boolean value) {
this.dryRun = value;
}
public Nil getNil() {
return nil;
}
public org.jruby.ir.operands.Boolean getTrue() {
return trueObject;
}
public org.jruby.ir.operands.Boolean getFalse() {
return falseObject;
}
public IRModuleBody getObject() {
return object;
}
public CompilerPassScheduler schedulePasses() {
return schedulePasses(compilerPasses);
}
public static CompilerPassScheduler schedulePasses(final List<CompilerPass> passes) {
CompilerPassScheduler scheduler = new CompilerPassScheduler() {
private Iterator<CompilerPass> iterator;
{
this.iterator = passes.iterator();
}
@Override
public Iterator<CompilerPass> iterator() {
return this.iterator;
}
};
return scheduler;
}
public List<CompilerPass> getCompilerPasses(IRScope scope) {
return compilerPasses;
}
public List<CompilerPass> getInliningCompilerPasses(IRScope scope) {
return inliningCompilerPasses;
}
public List<CompilerPass> getJITPasses(IRScope scope) {
return jitPasses;
}
public List<CompilerPass> getSafePasses(IRScope scope) {
return safePasses;
}
public Set<CompilerPassListener> getListeners() {
// FIXME: This is ugly but we want to conditionalize output based on JRuby module setting/unsetting
if (RubyInstanceConfig.IR_COMPILER_DEBUG) {
addListener(defaultListener);
} else {
removeListener(defaultListener);
}
return passListeners;
}
public InstructionsListener getInstructionsListener() {
return instrsListener;
}
public IRScopeListener getIRScopeListener() {
return irScopeListener;
}
public void addListener(CompilerPassListener listener) {
passListeners.add(listener);
}
public void removeListener(CompilerPassListener listener) {
passListeners.remove(listener);
}
public void addListener(InstructionsListener listener) {
if (RubyInstanceConfig.IR_COMPILER_DEBUG || RubyInstanceConfig.IR_VISUALIZER) {
if (instrsListener != null) {
throw new RuntimeException("InstructionsListener is set and other are currently not allowed");
}
instrsListener = listener;
}
}
public void removeListener(InstructionsListener listener) {
if (instrsListener.equals(listener)) instrsListener = null;
}
public void addListener(IRScopeListener listener) {
if (RubyInstanceConfig.IR_COMPILER_DEBUG || RubyInstanceConfig.IR_VISUALIZER) {
if (irScopeListener != null) {
throw new RuntimeException("IRScopeListener is set and other are currently not allowed");
}
irScopeListener = listener;
}
}
public void removeListener(IRScopeListener listener) {
if (irScopeListener.equals(listener)) irScopeListener = null;
}
public String getMetaClassName() {
return "<DUMMY_MC:" + dummyMetaClassCount++ + ">";
}
private TemporaryLocalVariable[] temporaryLocalVariables = new TemporaryLocalVariable[1600];
protected TemporaryLocalVariable[] growTemporaryVariablePool(int index) {
int newLength = index * 2;
TemporaryLocalVariable[] newPool = new TemporaryLocalVariable[newLength];
System.arraycopy(temporaryLocalVariables, 0, newPool, 0, temporaryLocalVariables.length);
temporaryLocalVariables = newPool;
return newPool;
}
// FIXME: Consider IRBuilder not using so many temporary variables for literal initialization. This is the
// vast majority of high index temp variables.
/**
* Temporary local variables are immutable and always start from a low index value and increment up
* to a higher index value per scope. So we can share these and store the ones in a simple list. If
* hard pinning is ever an issue we can periodically evict the list and start over at the cost of more
* live objects but this list cache reduces a simple empty Rails app console from over 140K instances
* to about 1200 instances.
*
*/
public TemporaryLocalVariable newTemporaryLocalVariable(int index) {
if (index >= temporaryLocalVariables.length-1) growTemporaryVariablePool(index);
TemporaryLocalVariable tempVar = temporaryLocalVariables[index];
if (tempVar == null) {
tempVar = new TemporaryLocalVariable(index);
temporaryLocalVariables[index] = tempVar;
}
return tempVar;
}
}