public Variable getRenamedVariable(Variable v) { Variable newVar = this.varRenameMap.get(v); if (newVar == null) { if (inClosureCloneMode) { // when cloning a closure, local vars and temps are not renamed newVar = v.cloneForCloningClosure(this); } else if (inClosureInlineMode) { // when inlining a closure, // - local var depths are reduced by 1 (to move them to the host scope) // - tmp vars are reallocated in the host scope if (v instanceof LocalVariable) { LocalVariable lv = (LocalVariable) v; int depth = lv.getScopeDepth(); newVar = getInlineHostScope().getLocalVariable(lv.getName(), depth > 1 ? depth - 1 : 0); } else { newVar = getInlineHostScope().getNewTemporaryVariable(); } } else { // when inlining a method, local vars and temps have to be renamed newVar = getInlineHostScope().getNewInlineVariable(inlineVarPrefix, v); } this.varRenameMap.put(v, newVar); } else if (inClosureCloneMode && (v instanceof LocalVariable)) { LocalVariable l_v = (LocalVariable) v; LocalVariable l_newVar = (LocalVariable) newVar; if (l_v.getScopeDepth() != l_newVar.getScopeDepth()) newVar = l_newVar.cloneForDepth(l_v.getScopeDepth()); } return newVar; }
public void addStoreAndBindingAllocInstructions(Set<LocalVariable> callsiteDirtyVars) { boolean addAllocateBindingInstructions = false; // SSS: This is going to be useful during JIT -- we are far away from there at this // time BindingStorePlacementProblem bsp = (BindingStorePlacementProblem) _prob; CFG cfg = bsp.getCFG(); IRExecutionScope s = cfg.getScope(); ListIterator<Instr> instrs = _bb.getInstrs().listIterator(); Set<LocalVariable> dirtyVars = new HashSet<LocalVariable>(_inDirtyVars); boolean bindingAllocated = _inBindingAllocated; // If this is the exit BB, we need a binding story 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 boolean amExitBB = (_bb == cfg.getExitBB()); if (amExitBB) { /** * LiveVariablesProblem lvp = * (LiveVariablesProblem)cfg.getDataFlowSolution(DataFlowConstants.LVP_NAME); * java.util.Collection<Variable> liveVars = lvp.getVarsLiveOnEntry(); * System.out.println("\n[In Exit BB] For CFG " + cfg + ":"); System.out.println("\t--> Dirty * vars here : " + java.util.Arrays.toString(dirtyVars.toArray())); System.out.println("\t--> * Vars live on entry: " + (liveVars == null ? "NONE" : * java.util.Arrays.toString(liveVars.toArray()))); liveVars = lvp.getVarsLiveOnExit(); * System.out.println("\t--> Vars live on exit : " + (liveVars == null ? "NONE" : * java.util.Arrays.toString(liveVars.toArray()))); */ LiveVariablesProblem lvp = (LiveVariablesProblem) cfg.getDataFlowSolution(DataFlowConstants.LVP_NAME); if (lvp != null) { java.util.Collection<Variable> liveVars = lvp.getVarsLiveOnExit(); 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(); if (i.operation == Operation.BINDING_LOAD) continue; if (i instanceof CallInstr) { CallInstr call = (CallInstr) i; Operand o = call.getClosureArg(); if ((o != null) && (o instanceof MetaObject)) { CFG cl_cfg = ((IRClosure) ((MetaObject) o).scope).getCFG(); BindingStorePlacementProblem cl_bsp = (BindingStorePlacementProblem) cl_cfg.getDataFlowSolution(bsp.getName()); instrs.previous(); if (addAllocateBindingInstructions) { // Add a binding allocation instruction, if necessary if (!bindingAllocated) { instrs.add(new AllocateBindingInstr(s)); bindingAllocated = true; } } // If the call is an eval, or if the callee can capture this method's binding, // we have to spill all variables. boolean spillAllVars = call.canBeEval() || call.targetRequiresCallersBinding(); // 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 (Variable v : dirtyVars) { if (spillAllVars || cl_bsp.scopeUsesVariable(v)) { // FIXME: This may not need check for local variable if it is guaranteed to only be // local variables. instrs.add(new StoreToBindingInstr(s, v.getName(), v)); newDirtyVars.remove(v); } // These variables will be spilt inside the closure -- so they will no longer be dirty // after the call site! else if (cl_bsp.scopeDefinesVariable(v)) { newDirtyVars.remove(v); } } dirtyVars = newDirtyVars; instrs.next(); // add stores in the closure ((BindingStorePlacementProblem) cl_cfg.getDataFlowSolution(bsp.getName())) .addStoreAndBindingAllocInstructions(); } // Call has no closure && it requires stores else if (call.targetRequiresCallersBinding()) { instrs.previous(); if (addAllocateBindingInstructions) { if (!bindingAllocated) { instrs.add(new AllocateBindingInstr(s)); bindingAllocated = true; } } for (LocalVariable v : dirtyVars) { instrs.add(new StoreToBindingInstr(s, v.getName(), v)); } instrs.next(); dirtyVars.clear(); } else if (call.canSetDollarVars()) { if (addAllocateBindingInstructions) { if (!bindingAllocated) { instrs.add(new AllocateBindingInstr(s)); bindingAllocated = true; } } } // Add all the remaining dirty local vars into callsiteDirtyVars // These variables would have to be spilled into the binding if this // call raised an exception and exited this scope. if ((callsiteDirtyVars != null) && call.canRaiseException()) callsiteDirtyVars.addAll(dirtyVars); } else if ((i instanceof ClosureReturnInstr) || (i instanceof BREAK_Instr)) { // 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; }; 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 (!amExitBB) { /** * LiveVariablesProblem lvp = * (LiveVariablesProblem)cfg.getDataFlowSolution(DataFlowConstants.LVP_NAME); * java.util.Collection<Variable> liveVars = lvp.getVarsLiveOnEntry(); * System.out.println("\n[@Closure Instr<" + i + ">] For CFG " + cfg + ":"); * System.out.println("\t--> Dirty vars here : " + * java.util.Arrays.toString(dirtyVars.toArray())); System.out.println("\t--> Vars live on * entry: " + (liveVars == null ? "NONE" : * java.util.Arrays.toString(liveVars.toArray()))); liveVars = lvp.getVarsLiveOnExit(); * System.out.println("\t--> Vars live on exit : " + (liveVars == null ? "NONE" : * java.util.Arrays.toString(liveVars.toArray()))); */ LiveVariablesProblem lvp = (LiveVariablesProblem) cfg.getDataFlowSolution(DataFlowConstants.LVP_NAME); if (lvp != null) { java.util.Collection<Variable> liveVars = lvp.getVarsLiveOnExit(); if (liveVars != null) { dirtyVars.retainAll( liveVars); // Intersection with variables live on exit from the scope } else { dirtyVars.clear(); } } } instrs.previous(); addClosureExitBindingStores(s, instrs, dirtyVars); instrs.next(); // Nothing is dirty anymore -- everything that needs spilling has been spilt dirtyVars.clear(); } Variable v = i.getResult(); // %self is local to every scope and never crosses scope boundaries and need not be // spilled/refilled if ((v != null) && (v instanceof LocalVariable) && !((LocalVariable) v).isSelf()) { dirtyVars.add((LocalVariable) v); } } // If this is the exit BB, add binding stores for all vars that are still dirty if (amExitBB) addClosureExitBindingStores(s, instrs, dirtyVars); }