package org.jruby.ir.passes;
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.interpreter.FullInterpreterContext;
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, FullInterpreterContext fic, Map<Operand, Operand> varRenameMap) {
if (varRenameMap.get(v) == null) varRenameMap.put(v, fic.getNewTemporaryVariableFor(v));
}
private void decrementScopeDepth(LocalVariable v, Map<Operand, Operand> varRenameMap) {
if (varRenameMap.get(v) == null) varRenameMap.put(v, v.cloneForDepth(v.getScopeDepth() - 1));
}
public void eliminateLocalVars(FullInterpreterContext fic) {
assert fic.getScope().getClosures().isEmpty() : "We assume that if a scope has nested closures, it uses a dynamic scoope.";
Map<Operand, Operand> varRenameMap = new HashMap<>();
fic.setDynamicScopeEliminated(true);
boolean parentScopeNeeded = false;
for (BasicBlock b: fic.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, fic, varRenameMap);
} else {
parentScopeNeeded = true;
decrementScopeDepth(lv, 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, fic, 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, varRenameMap);
}
}
}
}
}
if (parentScopeNeeded) fic.setReuseParentDynScope(true);
for (BasicBlock b: fic.getCFG().getBasicBlocks()) {
for (Instr i: b.getInstrs()) i.renameVars(varRenameMap);
}
(new LiveVariableAnalysis()).invalidate(fic);
}
@Override
public Object execute(FullInterpreterContext fic, Object... data) {
if (fic.getFlags().contains(IRFlags.REQUIRES_DYNSCOPE)) return null;
eliminateLocalVars(fic);
return null;
}
@Override
public boolean invalidate(FullInterpreterContext fic) {
return false;
}
}