private void updateUnboxedVarsInfo( Instr i, UnboxState state, Variable dst, boolean hasRescuer, boolean isDFBarrier) { HashSet<Variable> varsToBox = new HashSet<Variable>(); // Special treatment for instructions that can raise exceptions if (i.canRaiseException()) { if (hasRescuer) { // If we are going to be rescued, // box all unboxed dirty vars before we execute the instr. state.unboxedDirtyVars.clear(); } else { // We are going to exit if an exception is raised. // So, only need to bother with dirty live local vars for closures if (problem.getScope() instanceof IRClosure) { markLocalVariables(varsToBox, state.unboxedDirtyVars); } } } if (isDFBarrier) { // All dirty unboxed local vars will get reboxed. markLocalVariables(varsToBox, state.unboxedDirtyVars); // We have to re-unbox local variables as necessary since we don't // know how they are going to change once we get past this instruction. List<Variable> lvs = new ArrayList<Variable>(); markLocalVariables(lvs, state.unboxedVars.keySet()); state.unboxedVars.keySet().removeAll(lvs); } // Update set of unboxed dirty vars state.unboxedDirtyVars.removeAll(varsToBox); // FIXME: Also global variables .. see LVA / StoreLocalVar analysis. // B_TRUE and B_FALSE have unboxed forms and their operands // needn't get boxed back. Operation op = i.getOperation(); if (op != Operation.B_TRUE && op != Operation.B_FALSE) { // Vars used by this instruction that only exist in unboxed form // will have to get boxed before it is executed state.unboxedDirtyVars.removeAll(i.getUsedVariables()); } // If the instruction writes into 'dst', it will be in boxed form. if (dst != null) { state.unboxedVars.remove(dst); state.unboxedDirtyVars.remove(dst); } }
public void setUpUseDefLocalVarMaps() { definedLocalVars = new java.util.HashSet<Variable>(); usedLocalVars = new java.util.HashSet<Variable>(); for (BasicBlock bb : cfg().getBasicBlocks()) { for (Instr i : bb.getInstrs()) { for (Variable v : i.getUsedVariables()) { if (v instanceof LocalVariable) usedLocalVars.add(v); } if (i instanceof ResultInstr) { Variable v = ((ResultInstr) i).getResult(); if (v instanceof LocalVariable) definedLocalVars.add(v); } } } for (IRClosure cl : getClosures()) { cl.setUpUseDefLocalVarMaps(); } }
@Override public void applyTransferFunction(Instr i) { IRScope scope = problem.getScope(); boolean scopeBindingHasEscaped = scope.bindingHasEscaped(); // Right away, clear the variable defined by this instruction -- it doesn't have to be loaded! if (i instanceof ResultInstr) { reqdLoads.remove(((ResultInstr) i).getResult()); } // Process closure accepting instrs specially -- these are the sites of binding loads! if (i instanceof ClosureAcceptingInstr) { Operand o = ((ClosureAcceptingInstr) i).getClosureArg(); if (o != null && o instanceof WrappedIRClosure) { IRClosure cl = ((WrappedIRClosure) o).getClosure(); // Variables defined in the closure do not need to be loaded anymore at // program points before the call, because they will be loaded after the // call completes to fetch the latest value. // // Allocate a new hash-set and modify it to get around ConcurrentModificationException on // reqdLoads Set<LocalVariable> newReqdLoads = new HashSet<LocalVariable>(reqdLoads); for (LocalVariable v : reqdLoads) { if (cl.definesLocalVariable(v)) newReqdLoads.remove(v); } reqdLoads = newReqdLoads; } // In this case, we are going to blindly load everything -- so, at the call site, pending // loads dont carry over! if (scopeBindingHasEscaped) { reqdLoads.clear(); } else { // All variables not defined in the current scope have to be always loaded // because of multi-threading scenarios where some other scope // could update this variable concurrently. // // Allocate a new hash-set and modify it to get around ConcurrentModificationException on // reqdLoads Set<LocalVariable> newReqdLoads = new HashSet<LocalVariable>(reqdLoads); for (LocalVariable v : reqdLoads) { if (!scope.definesLocalVariable(v)) newReqdLoads.remove(v); } reqdLoads = newReqdLoads; } } else if (scopeBindingHasEscaped && (i.getOperation() == Operation.PUT_GLOBAL_VAR)) { // global-var tracing can execute closures set up in previous trace-var calls // in which case we would have the 'scopeBindingHasEscaped' flag set to true reqdLoads.clear(); } if (i.getOperation() == Operation.BINDING_STORE) { LocalVariable lv = ((StoreLocalVarInstr) i).getLocalVar(); if (!lv.isSelf()) reqdLoads.add(lv); } else { // The variables used as arguments will need to be loaded // %self is local to every scope and never crosses scope boundaries and need not be // spilled/refilled for (Variable x : i.getUsedVariables()) { if (x instanceof LocalVariable && !x.isSelf()) { reqdLoads.add((LocalVariable) x); } } } }
public void addLoads(Map<Operand, Operand> varRenameMap) { IRScope scope = problem.getScope(); boolean isEvalScript = scope instanceof IREvalScript; boolean scopeBindingHasEscaped = scope.bindingHasEscaped(); List<Instr> instrs = basicBlock.getInstrs(); ListIterator<Instr> it = instrs.listIterator(instrs.size()); initSolution(); while (it.hasPrevious()) { Instr i = it.previous(); // Right away, clear the variable defined by this instruction -- it doesn't have to be loaded! if (i instanceof ResultInstr) reqdLoads.remove(((ResultInstr) i).getResult()); // Process closure accepting instrs specially -- these are the sites of binding loads! if (i instanceof ClosureAcceptingInstr) { Operand o = ((ClosureAcceptingInstr) i).getClosureArg(); if (o != null && o instanceof WrappedIRClosure) { IRClosure cl = ((WrappedIRClosure) o).getClosure(); // Only those variables that are defined in the closure, and are in the required loads set // will need to be loaded from the binding after the call! Rest can wait .. // // Allocate a new hash-set and modify it to get around ConcurrentModificationException on // reqdLoads Set<LocalVariable> newReqdLoads = new HashSet<LocalVariable>(reqdLoads); it.next(); for (LocalVariable v : reqdLoads) { if (cl.definesLocalVariable(v)) { it.add( new LoadLocalVarInstr(scope, getLocalVarReplacement(v, scope, varRenameMap), v)); it.previous(); newReqdLoads.remove(v); } } it.previous(); reqdLoads = newReqdLoads; } // In this case, we are going to blindly load everything if (scopeBindingHasEscaped) { it.next(); for (LocalVariable v : reqdLoads) { it.add(new LoadLocalVarInstr(scope, getLocalVarReplacement(v, scope, varRenameMap), v)); it.previous(); } it.previous(); reqdLoads.clear(); } else { // All variables not defined in the current scope have to be always loaded // because of multi-threading scenarios where some other scope // could update this variable concurrently. // // Allocate a new hash-set and modify it to get around ConcurrentModificationException on // reqdLoads Set<LocalVariable> newReqdLoads = new HashSet<LocalVariable>(reqdLoads); it.next(); for (LocalVariable v : reqdLoads) { if (!scope.definesLocalVariable(v)) { it.add( new LoadLocalVarInstr(scope, getLocalVarReplacement(v, scope, varRenameMap), v)); it.previous(); newReqdLoads.remove(v); } } it.previous(); reqdLoads = newReqdLoads; } } else if (scopeBindingHasEscaped && (i.getOperation() == Operation.PUT_GLOBAL_VAR)) { // global-var tracing can execute closures set up in previous trace-var calls // in which case we would have the 'scopeBindingHasEscaped' flag set to true it.next(); for (LocalVariable v : reqdLoads) { it.add(new LoadLocalVarInstr(scope, getLocalVarReplacement(v, scope, varRenameMap), v)); it.previous(); } it.previous(); reqdLoads.clear(); } if (i.getOperation() == Operation.BINDING_STORE) { LocalVariable lv = ((StoreLocalVarInstr) i).getLocalVar(); if (!lv.isSelf()) { reqdLoads.add(lv); // SSS FIXME: Why is this reqd again? Document with example // Make sure there is a replacement var for all local vars getLocalVarReplacement(lv, scope, varRenameMap); } } else { // The variables used as arguments will need to be loaded // %self is local to every scope and never crosses scope boundaries and need not be // spilled/refilled for (Variable v : i.getUsedVariables()) { if (!(v instanceof LocalVariable)) continue; LocalVariable lv = (LocalVariable) v; if (!lv.isSelf()) { reqdLoads.add(lv); // SSS FIXME: Why is this reqd again? Document with example // Make sure there is a replacement var for all local vars getLocalVarReplacement(lv, scope, varRenameMap); } } } } // Add loads on entry of a rescue block. if (basicBlock.isRescueEntry()) { for (LocalVariable v : reqdLoads) { it.add(new LoadLocalVarInstr(scope, getLocalVarReplacement(v, scope, varRenameMap), v)); } } // Load first use of variables in closures if (scope instanceof IRClosure && basicBlock.isEntryBB()) { // System.out.println("\n[In Entry BB] For CFG " + getCFG() + ":"); // System.out.println("\t--> Reqd loads : " + // java.util.Arrays.toString(reqdLoads.toArray())); for (LocalVariable v : reqdLoads) { if (scope.usesLocalVariable(v) || scope.definesLocalVariable(v)) { if (isEvalScript || !(v instanceof ClosureLocalVariable) || (scope != ((ClosureLocalVariable) v).definingScope)) { it.add(new LoadLocalVarInstr(scope, getLocalVarReplacement(v, scope, varRenameMap), v)); } } } } }
private void boxRequiredVars( Instr i, UnboxState state, Map<Variable, TemporaryLocalVariable> unboxMap, Variable dst, boolean hasRescuer, boolean isDFBarrier, List<Instr> newInstrs) { // Special treatment for instructions that can raise exceptions boolean isClosure = this.problem.getScope() instanceof IRClosure; HashSet<Variable> varsToBox = new HashSet<Variable>(); if (i.canRaiseException()) { if (hasRescuer) { // If we are going to be rescued, // box all unboxed dirty vars before we execute the instr varsToBox.addAll(state.unboxedDirtyVars); } else if (isClosure) { // We are going to exit if an exception is raised. // So, only need to bother with dirty live local vars for closures for (Variable v : state.unboxedDirtyVars) { if (v instanceof LocalVariable) { varsToBox.add(v); } } } } if (isClosure && (i instanceof ReturnInstr || i instanceof BreakInstr)) { for (Variable v : state.unboxedDirtyVars) { if (v instanceof LocalVariable) { varsToBox.add(v); } } } if (isDFBarrier) { // All dirty unboxed (local) vars will get reboxed. for (Variable v : state.unboxedDirtyVars) { if (v instanceof LocalVariable) { varsToBox.add(v); } } // We have to re-unbox local variables as necessary since we don't // know how they are going to change once we get past this instruction. List<Variable> lvs = new ArrayList<Variable>(); for (Variable v : state.unboxedVars) { if (v instanceof LocalVariable) { lvs.add(v); } } state.unboxedVars.removeAll(lvs); } // B_TRUE and B_FALSE have unboxed forms and their operands // needn't get boxed back. Operation op = i.getOperation(); boolean isBranch = op == Operation.B_TRUE || op == Operation.B_FALSE; if (!isBranch) { // Vars used by this instruction that only exist in unboxed form // will have to get boxed before it is executed for (Variable v : i.getUsedVariables()) { if (state.unboxedDirtyVars.contains(v)) { varsToBox.add(v); } } } // Add boxing instrs. for (Variable v : varsToBox) { newInstrs.add(new BoxFloatInstr(v, getUnboxedVar(unboxMap, v))); state.unboxedDirtyVars.remove(v); } // Add 'i' itself if (isBranch) { OneOperandBranchInstr bi = (OneOperandBranchInstr) i; Operand a = bi.getArg1(); Operand ua = getUnboxedOperand(state.unboxedVars, unboxMap, a, newInstrs, false); if (ua == a) { newInstrs.add(i); } else if (op == Operation.B_TRUE) { newInstrs.add(new BTrueInstr(Operation.B_TRUE_UNBOXED, ua, bi.getJumpTarget())); } else { newInstrs.add(new BFalseInstr(Operation.B_FALSE_UNBOXED, ua, bi.getJumpTarget())); } } else { newInstrs.add(i); } // If the instruction writes into 'dst', it will be in boxed form. if (dst != null) { state.unboxedVars.remove(dst); state.unboxedDirtyVars.remove(dst); } }