Skip to content

Commit

Permalink
Merge branch 'master' into non-indy-jit
Browse files Browse the repository at this point in the history
Conflicts:
	core/src/main/java/org/jruby/ir/IRScope.java
  • Loading branch information
subbuss committed Oct 8, 2014
2 parents 5dda245 + a87c3a5 commit 72dc8b4
Show file tree
Hide file tree
Showing 16 changed files with 253 additions and 191 deletions.
7 changes: 3 additions & 4 deletions core/src/main/java/org/jruby/ir/IRFlags.java
Expand Up @@ -41,20 +41,19 @@ public enum IRFlags {
CAN_RECEIVE_NONLOCAL_RETURNS, // may receive a non-local return during execution
HAS_BREAK_INSTRS, // contains at least one break
HAS_END_BLOCKS, // has an end block. big de-opt flag
HAS_EXPLICIT_CALL_PROTOCOL, // contains call protocol instrs. if so we don't need to manage bindings frame implicitly.
HAS_EXPLICIT_CALL_PROTOCOL, // contains call protocol instrs => we don't need to manage bindings frame implicitly
HAS_LOOPS, // has a loop
HAS_NONLOCAL_RETURNS, // has a non-local return
HAS_OPTIMIZED_TEMPORARY_VARIABLES, // we simplified number of temp vars (before CFG was built). we can only do once.
HAS_UNUSED_IMPLICIT_BLOCK_ARG,// Is %block implicit block arg unused?
RECEIVES_CLOSURE_ARG, // This scope (or parent receives a closure
RECEIVES_KEYWORD_ARGS, // receives keyword args
REQUIRES_DYNSCOPE, // does this scope require a dynamic scope?
USES_BACKREF_OR_LASTLINE, // Since backref ($~) and lastline ($_) vars are allocated space on the dynamic scope.
USES_BACKREF_OR_LASTLINE, // Since backref ($~) and lastline ($_) vars are allocated space on the dynamic scope
USES_EVAL, // calls eval
USES_ZSUPER, // has zsuper instr
REQUIRES_FRAME, // callee may read/write caller's frame elements
REQUIRES_VISIBILITY, // callee may read/write caller's visibility

DYNSCOPE_ELIMINATED, // local var load/stores have been converted to tmp var accesses
REUSE_PARENT_DYNSCOPE, // for clsoures -- reuse parent's dynscope
REUSE_PARENT_DYNSCOPE, // for closures -- reuse parent's dynscope
}
6 changes: 3 additions & 3 deletions core/src/main/java/org/jruby/ir/IRManager.java
Expand Up @@ -17,9 +17,9 @@
/**
*/
public class IRManager {
public static String SAFE_COMPILER_PASSES = "LinearizeCFG";
public static String DEFAULT_COMPILER_PASSES = "OptimizeTempVarsPass,LocalOptimizationPass,LinearizeCFG";
public static String DEFAULT_JIT_PASSES = "AddLocalVarLoadStoreInstructions,AddCallProtocolInstructions,EnsureTempsAssigned,LinearizeCFG";
public static String SAFE_COMPILER_PASSES = "";
public static String DEFAULT_COMPILER_PASSES = "OptimizeTempVarsPass,LocalOptimizationPass";
public static String DEFAULT_JIT_PASSES = "AddLocalVarLoadStoreInstructions,AddCallProtocolInstructions,EnsureTempsAssigned";
public static String DEFAULT_INLINING_COMPILER_PASSES = "LocalOptimizationPass";

private int dummyMetaClassCount = 0;
Expand Down
77 changes: 39 additions & 38 deletions core/src/main/java/org/jruby/ir/IRScope.java
Expand Up @@ -11,6 +11,7 @@
import org.jruby.ir.passes.CompilerPass;
import org.jruby.ir.passes.CompilerPassScheduler;
import org.jruby.ir.passes.DeadCodeElimination;
import org.jruby.ir.passes.OptimizeDynScopesPass;
import org.jruby.ir.dataflow.analyses.StoreLocalVarPlacementProblem;
import org.jruby.ir.dataflow.analyses.LiveVariablesProblem;
import org.jruby.ir.passes.UnboxingPass;
Expand Down Expand Up @@ -105,6 +106,9 @@ public abstract class IRScope implements ParseResult {
/** Map of name -> dataflow problem */
private Map<String, DataFlowProblem> dfProbs;

/** What passes have been run on this scope? */
private List<CompilerPass> executedPasses;

private Instr[] linearizedInstrArray;
private List<BasicBlock> linearizedBBList;
private Map<Integer, Integer> rescueMap;
Expand Down Expand Up @@ -163,6 +167,8 @@ protected IRScope(IRScope s, IRScope lexicalParent) {
this.scopeId = globalScopeCount.getAndIncrement();
this.relinearizeCFG = false;

this.executedPasses = new ArrayList<CompilerPass>();

setupLexicalContainment();
}

Expand Down Expand Up @@ -208,6 +214,8 @@ public IRScope(IRManager manager, IRScope lexicalParent, String name,
this.scopeId = globalScopeCount.getAndIncrement();
this.relinearizeCFG = false;

this.executedPasses = new ArrayList<CompilerPass>();

setupLexicalContainment();
}

Expand Down Expand Up @@ -400,14 +408,6 @@ public boolean isNestedInClosure(IRClosure closure) {
return false;
}

public boolean hasHasOptimizedTemporaryVariables() {
return flags.contains(HAS_OPTIMIZED_TEMPORARY_VARIABLES);
}

public void setHasOptimizedTemporaryVariables() {
flags.add(HAS_OPTIMIZED_TEMPORARY_VARIABLES);
}

public void setHasLoopsFlag() {
flags.add(HAS_LOOPS);
}
Expand Down Expand Up @@ -546,6 +546,10 @@ private boolean isUnsafeScope() {
return unsafeScope;
}

public List<CompilerPass> getExecutedPasses() {
return executedPasses;
}

private void runCompilerPasses(List<CompilerPass> passes) {
// SSS FIXME: Why is this again? Document this weirdness!
// Forcibly clear out the shared eval-scope variable allocator each time this method executes
Expand All @@ -569,40 +573,28 @@ private void runCompilerPasses(List<CompilerPass> passes) {

CompilerPassScheduler scheduler = getManager().schedulePasses(passes);
for (CompilerPass pass: scheduler) {
if (pass.previouslyRun(this) == null) {
pass.run(this);
}
pass.run(this);
}

if (RubyInstanceConfig.IR_UNBOXING) {
(new UnboxingPass()).run(this);
}
}

private void runDeadCodeAndVarLoadStorePasses() {
// For scopes that don't require a dynamic scope,
// inline-add lvar loads/store to tmp-var loads/stores.
private void optimizeSimpleScopes() {
// For safe scopes that don't require a dynamic scope,
// run DCE since the analysis is less likely to be
// stymied by escaped bindings.
if (!isUnsafeScope() && !flags.contains(REQUIRES_DYNSCOPE)) {
CompilerPass pass;
pass = new DeadCodeElimination();
if (pass.previouslyRun(this) == null) {
pass.run(this);
}

// This will run the simplified version of the pass
// that doesn't require dataflow analysis and hence
// can run on closures independent of enclosing scopes.
pass = new AddLocalVarLoadStoreInstructions();
if (pass.previouslyRun(this) == null) {
((AddLocalVarLoadStoreInstructions)pass).eliminateLocalVars(this);
setDataFlowSolution(StoreLocalVarPlacementProblem.NAME, new StoreLocalVarPlacementProblem());
setDataFlowSolution(LiveVariablesProblem.NAME, null);
}
(new DeadCodeElimination()).run(this);
(new OptimizeDynScopesPass()).run(this);
}
}

/** Run any necessary passes to get the IR ready for interpretation */
public synchronized Instr[] prepareForInterpretation(boolean isLambda) {
public void initScope(boolean isLambda) {
// Reset linearization, if any exists
resetLinearizationData();

// Build CFG and run compiler passes, if necessary
if (getCFG() == null) {
buildCFG();
Expand All @@ -623,22 +615,35 @@ public synchronized Instr[] prepareForInterpretation(boolean isLambda) {
// Run DCE and var load/store passes where applicable
// But, if we have been passed in a list of passes to run
// on the commandline, skip this opt.
runDeadCodeAndVarLoadStorePasses();
optimizeSimpleScopes();
}
}

/** Run any necessary passes to get the IR ready for interpretation */
public synchronized Instr[] prepareForInterpretation(boolean isLambda) {
initScope(isLambda);

checkRelinearization();

if (linearizedInstrArray != null) return linearizedInstrArray;

// System.out.println("-- passes run for: " + this + " = " + java.util.Arrays.toString(executedPasses.toArray()));

// Linearize CFG, etc.
return prepareInstructions();
}

/* SSS FIXME: Do we need to synchronize on this? Cache this info in a scope field? */
/** Run any necessary passes to get the IR ready for compilation */
public synchronized List<BasicBlock> prepareForCompilation() {
// Reset linearization, since we will add JIT-specific flow and instrs
resetLinearizationData();
// For lambdas, we need to add a global ensure block to catch
// uncaught breaks and throw a LocalJumpError.
//
// Since we dont re-JIT a previously JIT-ted closure,
// mark all closures lambdas always. But, check if there are
// other smarts available to us and eliminate adding
// this code to every closure there is.
initScope(this instanceof IRClosure);

runCompilerPasses(getManager().getJITPasses(this));

Expand Down Expand Up @@ -1136,10 +1141,6 @@ public void inlineMethod(IRScope method, RubyModule implClass, int classToken, B
}
}

public void resetCFG() {
cfg = null;
}

/* Record a begin block -- not all scope implementations can handle them */
public void recordBeginBlock(IRClosure beginBlockClosure) {
throw new RuntimeException("BEGIN blocks cannot be added to: " + this.getClass().getName());
Expand Down
17 changes: 17 additions & 0 deletions core/src/main/java/org/jruby/ir/instructions/CallBase.java
Expand Up @@ -164,6 +164,23 @@ public boolean computeScopeFlags(IRScope scope) {
}
}

// Kernel.local_variables inspects variables.
// and JRuby implementation uses dyn-scope to access the static-scope
// to output the local variables => we cannot strip dynscope in those cases.
// FIXME: We need to decouple static-scope and dyn-scope.
String mname = getMethodAddr().getName();
if (mname.equals("local_variables")) {
scope.getFlags().add(REQUIRES_DYNSCOPE);
} else if (mname.equals("send") || mname.equals("__send__")) {
Operand[] args = getCallArgs();
if (args.length >= 1) {
Operand meth = args[0];
if (meth instanceof StringLiteral && "local_variables".equals(((StringLiteral)meth).string)) {
scope.getFlags().add(REQUIRES_DYNSCOPE);
}
}
}

return modifiedScope;
}
/**
Expand Down
Expand Up @@ -161,11 +161,6 @@ public Object execute(IRScope scope, Object... data) {
return null;
}

@Override
public Object previouslyRun(IRScope scope) {
return scope.hasExplicitCallProtocol() ? new Object() : null;
}

@Override
public void invalidate(IRScope scope) {
// Cannot add call protocol instructions after we've added them once.
Expand Down
@@ -1,16 +1,12 @@
package org.jruby.ir.passes;

import org.jruby.ir.IRClosure;
import org.jruby.ir.IRScope;
import org.jruby.ir.IRFlags;
import org.jruby.ir.IRScope;
import org.jruby.ir.dataflow.analyses.LiveVariablesProblem;
import org.jruby.ir.dataflow.analyses.LoadLocalVarPlacementProblem;
import org.jruby.ir.dataflow.analyses.StoreLocalVarPlacementProblem;
import org.jruby.ir.instructions.Instr;
import org.jruby.ir.instructions.ResultInstr;
import org.jruby.ir.instructions.LoadLocalVarInstr;
import org.jruby.ir.instructions.StoreLocalVarInstr;
import org.jruby.ir.operands.LocalVariable;
import org.jruby.ir.operands.Operand;
import org.jruby.ir.operands.Variable;
import org.jruby.ir.representations.BasicBlock;
Expand All @@ -33,88 +29,15 @@ public List<Class<? extends CompilerPass>> getDependencies() {
return DEPENDENCIES;
}

private void setupLocalVarReplacement(LocalVariable v, IRScope s, Map<Operand, Operand> varRenameMap) {
if (varRenameMap.get(v) == null) varRenameMap.put(v, s.getNewTemporaryVariableFor(v));
}

private void decrementScopeDepth(LocalVariable v, IRScope s, Map<Operand, Operand> varRenameMap) {
if (varRenameMap.get(v) == null) varRenameMap.put(v, v.cloneForDepth(v.getScopeDepth()-1));
}

public void eliminateLocalVars(IRScope s) {
Map<Operand, Operand> varRenameMap = new HashMap<Operand, Operand>();
// Record the fact that we eliminated the scope
s.getFlags().add(IRFlags.DYNSCOPE_ELIMINATED);

// Since the scope does not require a binding, no need to do
// any analysis. It is sufficient to rename all local var uses
// with a temporary variable.
boolean parentScopeNeeded = false;
for (BasicBlock b: s.getCFG().getBasicBlocks()) {
for (Instr i: b.getInstrs()) {
if (i instanceof ResultInstr) {
Variable v = ((ResultInstr) i).getResult();
// %self is local to every scope and never crosses scope boundaries and need not be spilled/refilled
if (v instanceof LocalVariable && !v.isSelf()) {
LocalVariable lv = (LocalVariable)v;
if (lv.getScopeDepth() == 0) {
// Make sure there is a replacement tmp-var allocated for lv
setupLocalVarReplacement(lv, s, varRenameMap);
} else {
parentScopeNeeded = true;
decrementScopeDepth(lv, s, varRenameMap);
}
}
}

for (Variable v : i.getUsedVariables()) {
if (v instanceof LocalVariable && !v.isSelf()) {
LocalVariable lv = (LocalVariable)v;
if (lv.getScopeDepth() == 0) {
// Make sure there is a replacement tmp-var allocated for lv
setupLocalVarReplacement(lv, s, varRenameMap);
} else {
// SSS FIXME: Ugly/Dirty! Some abstraction is broken.
if (i instanceof LoadLocalVarInstr) {
LoadLocalVarInstr llvi = (LoadLocalVarInstr)i;
if (llvi.getLocalVar() == lv) {
llvi.decrementLVarScopeDepth();
}
} else if (i instanceof StoreLocalVarInstr) {
StoreLocalVarInstr slvi = (StoreLocalVarInstr)i;
if (slvi.getLocalVar() == lv) {
slvi.decrementLVarScopeDepth();
}
}

parentScopeNeeded = true;
decrementScopeDepth(lv, s, varRenameMap);
}
}
}
}
}

if (parentScopeNeeded) {
s.getFlags().add(IRFlags.REUSE_PARENT_DYNSCOPE);
}

// Rename all local var uses with their tmp-var stand-ins
for (BasicBlock b: s.getCFG().getBasicBlocks()) {
for (Instr i: b.getInstrs()) i.renameVars(varRenameMap);
}
}

@Override
public Object execute(IRScope s, Object... data) {
StoreLocalVarPlacementProblem slvp = new StoreLocalVarPlacementProblem();

// Make sure flags are computed
s.computeScopeFlags();
// No need to run this pass if we eliminated the dynamic scope!
if (!s.getFlags().contains(IRFlags.DYNSCOPE_ELIMINATED) || s.getFlags().contains(IRFlags.REUSE_PARENT_DYNSCOPE)) {
// Make sure flags are computed
s.computeScopeFlags();

if (!s.getFlags().contains(IRFlags.REQUIRES_DYNSCOPE) && (data.length == 0 || data[0] != Boolean.TRUE)) {
eliminateLocalVars(s);
} else {
Map<Operand, Operand> varRenameMap = new HashMap<Operand, Operand>();
// 1. Figure out required stores
// 2. Add stores
Expand Down Expand Up @@ -145,7 +68,7 @@ public Object execute(IRScope s, Object... data) {
//
// In the current implementation, nested scopes are processed independently (unlike Live Variable Analysis)
// However, since this pass requires LVA information, in reality, we cannot run
for (IRClosure c: s.getClosures()) execute(c, Boolean.TRUE);
for (IRClosure c: s.getClosures()) execute(c);
}

s.setDataFlowSolution(StoreLocalVarPlacementProblem.NAME, slvp);
Expand All @@ -163,6 +86,7 @@ public Object previouslyRun(IRScope scope) {

@Override
public void invalidate(IRScope scope) {
super.invalidate(scope);
scope.setDataFlowSolution(StoreLocalVarPlacementProblem.NAME, null);
}
}
11 changes: 10 additions & 1 deletion core/src/main/java/org/jruby/ir/passes/CFGBuilder.java
Expand Up @@ -2,6 +2,15 @@

import org.jruby.ir.IRScope;

/**
* CFGBuilder is mainly a pass to be lazy. We do not want to build CFG for scopes which are never called.
*
* Once we have a CFG that is the base data structure where we interact with instructions. The original
* list of instructions from IRBuilder is no longer important. This is also why this pass is incapable
* of invalidating the CFG.
*/


public class CFGBuilder extends CompilerPass {
@Override
public String getLabel() {
Expand All @@ -20,6 +29,6 @@ public Object execute(IRScope scope, Object... data) {

@Override
public void invalidate(IRScope scope) {
scope.resetCFG();
// CFG is primal information to a scope and cannot be recreated once generated.
}
}

0 comments on commit 72dc8b4

Please sign in to comment.