public String toString() {
return "";
}
public boolean addStores(Map<Operand, Operand> varRenameMap, Set<LocalVariable> excTargetDirtyVars) {
IRScope scope = problem.getScope();
boolean addedStores = false;
boolean isClosure = scope instanceof IRClosure;
boolean scopeBindingHasEscaped = scope.bindingHasEscaped();
ListIterator<Instr> instrs = basicBlock.getInstrs().listIterator();
initSolution();
// If this is the exit BB, we need a binding store on exit only for vars that are both:
//
// (a) dirty,
// (b) live on exit from the closure
// condition reqd. because the variable could be dirty but not used outside.
// Ex: s=0; a.each { |i| j = i+1; sum += j; }; puts sum
// i,j are dirty inside the block, but not used outside
if (basicBlock.isExitBB()) {
LiveVariablesProblem lvp = (LiveVariablesProblem)scope.getDataFlowSolution(DataFlowConstants.LVP_NAME);
java.util.Collection<LocalVariable> liveVars = lvp.getVarsLiveOnScopeExit();
if (liveVars != null) {
dirtyVars.retainAll(liveVars); // Intersection with variables live on exit from the scope
} else {
dirtyVars.clear();
}
}
while (instrs.hasNext()) {
Instr i = instrs.next();
// Process closure accepting instrs specially -- these are the sites of binding stores!
if (i instanceof ClosureAcceptingInstr) {
Operand o = ((ClosureAcceptingInstr)i).getClosureArg();
if (o != null && o instanceof WrappedIRClosure) {
IRClosure cl = ((WrappedIRClosure) o).getClosure();
// Add before call -- hence instrs.previous & instrs.next
instrs.previous();
// If the call is a dataflow barrier, we have to spill everything here
boolean spillAllVars = scopeBindingHasEscaped;
// Unless we have to spill everything, spill only those dirty variables that are:
// - used in the closure (FIXME: Strictly only those vars that are live at the call site -- but we dont have this info!)
Set<LocalVariable> newDirtyVars = new HashSet<LocalVariable>(dirtyVars);
for (LocalVariable v : dirtyVars) {
// We have to spill the var that is defined in the closure as well because the load var pass
// will attempt to load the var always. So, if the call doesn't actually call the closure,
// we'll be in trouble in that scenario!
if (spillAllVars || cl.usesLocalVariable(v) || cl.definesLocalVariable(v)) {
addedStores = true;
instrs.add(new StoreLocalVarInstr(problem.getLocalVarReplacement(v, varRenameMap), scope, v));
newDirtyVars.remove(v);
}
}
dirtyVars = newDirtyVars;
instrs.next();
} else if (scopeBindingHasEscaped) { // Call has no closure && it requires stores
// Add before call -- hence instrs.previous & instrs.next
instrs.previous();
for (LocalVariable v : dirtyVars) {
addedStores = true;
instrs.add(new StoreLocalVarInstr(problem.getLocalVarReplacement(v, varRenameMap), scope, v));
}
instrs.next();
dirtyVars.clear();
} else {
instrs.previous();
// All variables not local to the current scope have to be always spilled because of
// multi-threading scenarios where some other scope could load this variable concurrently.
//
// Allocate a new hash-set and modify it to get around ConcurrentModificationException on dirtyVars
Set<LocalVariable> newDirtyVars = new HashSet<LocalVariable>(dirtyVars);
for (LocalVariable v : dirtyVars) {
// SSS FIXME: I guess we cannot use v.getScopeDepth() > 0 because the variable could be a cloned
// instance from a different depth and that could mislead us. See if there is a way to fix this.
// If we introduced 'definingScope' in all local variables, we could simply check for scope match
// without the instanceof check here.
if ( (v instanceof ClosureLocalVariable && ((ClosureLocalVariable)v).definingScope != scope)
|| (!(v instanceof ClosureLocalVariable) && scope.getScopeType().isClosureType()))
{
addedStores = true;
instrs.add(new StoreLocalVarInstr(problem.getLocalVarReplacement(v, varRenameMap), scope, v));
newDirtyVars.remove(v);
}
}
dirtyVars = newDirtyVars;
instrs.next();
}
} else if ((isClosure && (i instanceof ReturnInstr)) || (i instanceof BreakInstr)) {
// At closure return and break instructions (both of which are exits from the closure),
// we need a binding store on exit only for vars that are both:
//
// (a) dirty,
// (b) live on exit from the closure
// condition reqd. because the variable could be dirty but not used outside.
// Ex: s=0; a.each { |i| j = i+1; sum += j; return if j < 5; sum += 1; }; puts sum
// i,j are dirty inside the block, but not used outside
//
// If this also happens to be exit BB, we would have intersected already earlier -- so no need to do it again!
if (!basicBlock.isExitBB()) {
LiveVariablesProblem lvp = (LiveVariablesProblem)scope.getDataFlowSolution(DataFlowConstants.LVP_NAME);
if (lvp == null) {
System.out.println("LVP missing for " + scope);
}
java.util.Collection<LocalVariable> liveVars = lvp.getVarsLiveOnScopeExit();
if (liveVars != null) {