Skip to content

Commit

Permalink
Fix LiveVariableAnalysis for standalone runs on closures.
Browse files Browse the repository at this point in the history
* So far, LVA could only be initiated on methods -- in turn, nested
  closures would get LVA run on them.

* However, in upcoming patches, we might end up running LVA on
  closures independently because of dependency issues. Rather than
  try to work around those, it is simpler to allow LVA to run on
  closures independently.

* Fixed/pre-empted a possible crasher in LVA for nested clossures.
  • Loading branch information
subbuss committed Oct 6, 2014
1 parent a5a6787 commit 2689915
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 1 deletion.
Expand Up @@ -108,9 +108,11 @@ public void applyTransferFunction(Instr i) {
if (o != null && o instanceof WrappedIRClosure) {
IRClosure cl = ((WrappedIRClosure)o).getClosure();
LiveVariablesProblem cl_lvp = (LiveVariablesProblem) cl.getDataFlowSolution(DataFlowConstants.LVP_NAME);
boolean needsInit = false;
if (cl_lvp == null) {
cl_lvp = new LiveVariablesProblem(cl, problem.getNonSelfLocalVars());
cl.setDataFlowSolution(cl_lvp.getName(), cl_lvp);
needsInit = true;
}

// Add all living local variables.
Expand Down Expand Up @@ -143,6 +145,12 @@ public void applyTransferFunction(Instr i) {
// SSS FIXME: Think through this .. Is there any way out of having
// to recompute the entire lva for the closure each time through?
cl_lvp.setVarsLiveOnScopeExit(liveVars);
if (needsInit) {
// Init DF vars from this set
for (Variable v: liveVars) {
cl_lvp.addDFVar(v);
}
}
cl_lvp.compute_MOP_Solution();

// Check if liveOnScopeEntry added new vars -- if so, rerun.
Expand Down
55 changes: 54 additions & 1 deletion core/src/main/java/org/jruby/ir/passes/LiveVariableAnalysis.java
@@ -1,9 +1,21 @@
package org.jruby.ir.passes;

import org.jruby.ir.IRClosure;
import org.jruby.ir.IRScope;
import org.jruby.ir.instructions.ClosureAcceptingInstr;
import org.jruby.ir.instructions.Instr;
import org.jruby.ir.instructions.ResultInstr;
import org.jruby.ir.operands.LocalVariable;
import org.jruby.ir.operands.Operand;
import org.jruby.ir.operands.Variable;
import org.jruby.ir.operands.WrappedIRClosure;
import org.jruby.ir.representations.BasicBlock;
import org.jruby.ir.dataflow.analyses.LiveVariablesProblem;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.List;

public class LiveVariableAnalysis extends CompilerPass {
Expand All @@ -24,11 +36,52 @@ public Object previouslyRun(IRScope scope) {
return scope.getDataFlowSolution(LiveVariablesProblem.NAME);
}

private void collectNonLocalDirtyVars(IRClosure cl, Set<LocalVariable> vars, int minDepth) {
for (BasicBlock bb: cl.cfg().getBasicBlocks()) {
for (Instr i: bb.getInstrs()) {
// Collect local vars belonging to an outer scope dirtied here
if (i instanceof ResultInstr) {
Variable res = ((ResultInstr)i).getResult();
if (res instanceof LocalVariable && ((LocalVariable)res).getScopeDepth() > minDepth) {
vars.add((LocalVariable)res);
}
}

// When encountering nested closures, increase minDepth by 1
// so that we continue to collect vars belong to outer scopes.
if (i instanceof ClosureAcceptingInstr) {
Operand clArg = ((ClosureAcceptingInstr)i).getClosureArg();
if (clArg instanceof WrappedIRClosure) {
collectNonLocalDirtyVars(((WrappedIRClosure)clArg).getClosure(), vars, minDepth+1);
}
}
}
}
}

@Override
public Object execute(IRScope scope, Object... data) {
LiveVariablesProblem lvp = new LiveVariablesProblem(scope);
lvp.compute_MOP_Solution();

if (scope instanceof IRClosure) {
// Go conservative! Normally, closure scopes are analyzed
// in the context of their outermost method scopes where we
// have better knowledge of aliveness in that global context.
//
// But, if we are analyzing closures standalone, we have to
// conservatively assume that any dirtied variables that
// belong to an outer scope are live on exit.
Set<LocalVariable> nlVars = new HashSet<LocalVariable>();
collectNonLocalDirtyVars((IRClosure)scope, nlVars, 0);

// Init DF vars from this set
for (Variable v: nlVars) {
lvp.addDFVar(v);
}
lvp.setVarsLiveOnScopeExit(nlVars);
}

lvp.compute_MOP_Solution();
scope.setDataFlowSolution(LiveVariablesProblem.NAME, lvp);

return lvp;
Expand Down

0 comments on commit 2689915

Please sign in to comment.