package org.jruby.ir.passes;
import java.util.EnumSet;
import org.jruby.ir.IRScope;
import org.jruby.ir.IRFlags;
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;
import java.util.HashMap;
import java.util.ListIterator;
import java.util.Map;
public class OptimizeDynScopesPass extends CompilerPass {
@Override
public String getLabel() {
return "Optimize Dynamic Scopes";
}
@Override
public String getShortLabel() {
return "Opt Dyn Scopes";
}
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) {
assert s.getClosures().isEmpty() : "We assume that if a scope has nested closures, it uses a dynamic scoope.";
Map<Operand, Operand> varRenameMap = new HashMap<>();
EnumSet<IRFlags> flags = s.getExecutionContext().getFlags();
flags.add(IRFlags.DYNSCOPE_ELIMINATED);
boolean parentScopeNeeded = false;
for (BasicBlock b: s.getCFG().getBasicBlocks()) {
ListIterator<Instr> instrs = b.getInstrs().listIterator();
while (instrs.hasNext()) {
Instr i = instrs.next();
if (i instanceof ResultInstr) {
Variable v = ((ResultInstr) i).getResult();
if (v instanceof LocalVariable && !v.isSelf()) {
LocalVariable lv = (LocalVariable)v;
if (lv.getScopeDepth() == 0) {
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) {
if (i instanceof LoadLocalVarInstr) {
LoadLocalVarInstr llvi = (LoadLocalVarInstr)i;
if (llvi.getLocalVar() == lv) {
instrs.remove();
}
} else if (i instanceof StoreLocalVarInstr) {
StoreLocalVarInstr slvi = (StoreLocalVarInstr)i;
if (slvi.getLocalVar() == lv) {
instrs.remove();
}
}
setupLocalVarReplacement(lv, s, varRenameMap);
} else {
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) flags.add(IRFlags.REUSE_PARENT_DYNSCOPE);
for (BasicBlock b: s.getCFG().getBasicBlocks()) {
for (Instr i: b.getInstrs()) i.renameVars(varRenameMap);
}
(new LiveVariableAnalysis()).invalidate(s);
}
@Override
public Object execute(IRScope scope, Object... data) {
scope.computeScopeFlags();
if (scope.getExecutionContext().getFlags().contains(IRFlags.REQUIRES_DYNSCOPE)) return null;
eliminateLocalVars(scope);
return null;
}
@Override
public boolean invalidate(IRScope scope) {
return false;
}
}