// Partially evaluate the programList with respect to the given static inputs, // producing a new ProgramLines object. public ProgramLines PEval(Value[] args, FullCellAddr[] residualInputs) { PEnv pEnv = new PEnv(); // Map static input cells to their constant values: for (int i = 0; i < args.Length; i++) { pEnv[inputCells[i]] = CGConst.Make(args[i]); } ProgramLines residual = new ProgramLines(outputCell, residualInputs); // PE-time environment PEnv maps each residual input cell address to the delegate argument: for (int i = 0; i < residualInputs.Length; i++) { FullCellAddr input = residualInputs[i]; pEnv[input] = new CGCellRef(input, residual.addressToVariable[input]); } // Process the given function's compute cells in dependency order, output last: foreach (ComputeCell ccell in programList) { ComputeCell rCcell = ccell.PEval(pEnv); if (rCcell != null) { residual.AddComputeCell(ccell.cellAddr, rCcell); } } residual = residual.PruneZeroUseCells(); return(residual); }
/// <summary> /// Compiles the topologically sorted list of Expr to a list (program) /// of ComputeCells, encapsulating CGExprs. Builds a map from cellAddr to /// local variable ids, for compiling sheet-internal cellrefs to ldloc instructions. /// </summary> public void AddComputeCells(DependencyGraph dpGraph, IList <FullCellAddr> cellList) { Debug.Assert(dpGraph.outputCell == cellList[cellList.Count - 1]); CGExpr outputExpr; if (cellList.Count == 0 || cellList.Count == 1 && dpGraph.inputCellSet.Contains(cellList.Single())) { // The output cell is also an input cell; load it: outputExpr = new CGCellRef(dpGraph.outputCell, addressToVariable[dpGraph.outputCell]); } else { // First process all non-output cells, and ignore all input cells: foreach (FullCellAddr cellAddr in cellList) { if (cellAddr.Equals(dpGraph.outputCell)) { continue; } HashSet <FullCellAddr> dependents = dpGraph.GetDependents(cellAddr); int minUses = dependents.Count; if (minUses == 1) { FullCellAddr fromFca = dependents.First(); minUses = Math.Max(minUses, GetCount(fromFca, cellAddr)); } // Now if minUses==1 then there is at most one use of the cell at cellAddr, // and no local variable is needed. Otherwise, allocate a local variable: if (minUses > 1) { CGExpr newExpr = CGExpressionBuilder.BuildExpression(cellAddr, addressToVariable); Variable var = new LocalVariable(cellAddr.ToString(), newExpr.Type()); AddComputeCell(cellAddr, new ComputeCell(newExpr, var, cellAddr)); } } // Then process the output cell: outputExpr = CGExpressionBuilder.BuildExpression(dpGraph.outputCell, addressToVariable); } // Add the output cell expression last, without a variable to bind it to; hence the null, // also indicating that (only) the output cell is in tail position: AddComputeCell(dpGraph.outputCell, new ComputeCell(outputExpr, null, dpGraph.outputCell)); }
// Returns residual ComputeCell or null if no cell needed public ComputeCell PEval(PEnv pEnv) { CGExpr rCond = null; if (evalCond != null) // Never the case for an output cell { rCond = evalCond.PEval(pEnv, false /* not dynamic control */); } if (rCond is CGNumberConst) { if ((rCond as CGNumberConst).number.value != 0.0) { rCond = null; // eval cond constant TRUE, discard eval cond } else { return(null); // eval cond constant FALSE, discard entire compute cell } } // If residual eval cond is not TRUE then expr has dynamic control CGExpr rExpr = expr.PEval(pEnv, rCond != null); if (rExpr is CGConst && var != null) { // If cell's value is constant and it is not an output cell just put in PEnv pEnv[cellAddr] = rExpr; return(null); } else { // Else create fresh local variable for the residual cell, and make // PEnv map cell address to that local variable: Variable newVar = var != null?var.Fresh() : null; pEnv[cellAddr] = new CGCellRef(cellAddr, newVar); ComputeCell result = new ComputeCell(rExpr, newVar, cellAddr); // result.EvalCond = rCond; // Don't save residual eval cond, we compute it accurately later... return(result); } }
// Returns residual ComputeCell or null if no cell needed public ComputeCell PEval(PEnv pEnv) { CGExpr rCond = null; if (evalCond != null) // Never the case for an output cell { rCond = evalCond.PEval(pEnv, false /* not dynamic control */); } if (rCond is CGNumberConst) { if ((rCond as CGNumberConst).number.value != 0.0) { rCond = null; // eval cond constant TRUE, discard eval cond } else { return null; // eval cond constant FALSE, discard entire compute cell } } // If residual eval cond is not TRUE then expr has dynamic control CGExpr rExpr = expr.PEval(pEnv, rCond != null); if (rExpr is CGConst && var != null) { // If cell's value is constant and it is not an output cell just put in PEnv pEnv[cellAddr] = rExpr; return null; } else { // Else create fresh local variable for the residual cell, and make // PEnv map cell address to that local variable: Variable newVar = var != null ? var.Fresh() : null; pEnv[cellAddr] = new CGCellRef(cellAddr, newVar); ComputeCell result = new ComputeCell(rExpr, newVar, cellAddr); // result.EvalCond = rCond; // Don't save residual eval cond, we compute it accurately later... return result; } }
// Partially evaluate the programList with respect to the given static inputs, // producing a new ProgramLines object. public ProgramLines PEval(Value[] args, FullCellAddr[] residualInputs) { PEnv pEnv = new PEnv(); // Map static input cells to their constant values: for (int i = 0; i < args.Length; i++) { pEnv[inputCells[i]] = CGConst.Make(args[i]); } ProgramLines residual = new ProgramLines(outputCell, residualInputs); // PE-time environment PEnv maps each residual input cell address to the delegate argument: for (int i = 0; i < residualInputs.Length; i++) { FullCellAddr input = residualInputs[i]; pEnv[input] = new CGCellRef(input, residual.addressToVariable[input]); } // Process the given function's compute cells in dependency order, output last: foreach (ComputeCell ccell in programList) { ComputeCell rCcell = ccell.PEval(pEnv); if (rCcell != null) { residual.AddComputeCell(ccell.cellAddr, rCcell); } } residual = residual.PruneZeroUseCells(); return residual; }
/// <summary> /// Compiles the topologically sorted list of Expr to a list (program) /// of ComputeCells, encapsulating CGExprs. Builds a map from cellAddr to /// local variable ids, for compiling sheet-internal cellrefs to ldloc instructions. /// </summary> public void AddComputeCells(DependencyGraph dpGraph, IList<FullCellAddr> cellList) { Debug.Assert(dpGraph.outputCell == cellList[cellList.Count - 1]); CGExpr outputExpr; if (cellList.Count == 0 || cellList.Count == 1 && dpGraph.inputCellSet.Contains(cellList.Single())) { // The output cell is also an input cell; load it: outputExpr = new CGCellRef(dpGraph.outputCell, addressToVariable[dpGraph.outputCell]); } else { // First process all non-output cells, and ignore all input cells: foreach (FullCellAddr cellAddr in cellList) { if (cellAddr.Equals(dpGraph.outputCell)) { continue; } HashSet<FullCellAddr> dependents = dpGraph.GetDependents(cellAddr); int minUses = dependents.Count; if (minUses == 1) { FullCellAddr fromFca = dependents.First(); minUses = Math.Max(minUses, GetCount(fromFca, cellAddr)); } // Now if minUses==1 then there is at most one use of the cell at cellAddr, // and no local variable is needed. Otherwise, allocate a local variable: if (minUses > 1) { CGExpr newExpr = CGExpressionBuilder.BuildExpression(cellAddr, addressToVariable); Variable var = new LocalVariable(cellAddr.ToString(), newExpr.Type()); AddComputeCell(cellAddr, new ComputeCell(newExpr, var, cellAddr)); } } // Then process the output cell: outputExpr = CGExpressionBuilder.BuildExpression(dpGraph.outputCell, addressToVariable); } // Add the output cell expression last, without a variable to bind it to; hence the null, // also indicating that (only) the output cell is in tail position: AddComputeCell(dpGraph.outputCell, new ComputeCell(outputExpr, null, dpGraph.outputCell)); }