/// <summary> /// Removes unassigned declared variables and replaces use sites by default expressions. /// </summary> /// <param name="block">The block to remove unassigned variables from.</param> /// <returns>The result of removing unassigned variables.</returns> /// <example> /// <code>{ int x, int y; x = 1; x + y; }</code> /// becomes /// <code>{ int x; x = 1; x + default(int); }</code> /// </example> private BlockExpression RemoveUnassignedVariables(BlockExpression block) { var oldVariables = block.Variables; // REVIEW: This does a lot of repeated scanning of the same child nodes. // We could consider storing these results for later reuse. if (oldVariables.Count > 0) { var analyzer = new AssignmentAnalyzer(oldVariables); analyzer.Visit(block.Expressions); var unassigned = analyzer.Unassigned; if (unassigned.Count > 0) { var subst = new VariableDefaultValueSubstitutor(unassigned); var newExpressions = subst.Visit(block.Expressions); // // NB: The introduction of default values can yield more optimization // opportunities, so we visit the child expressions again. // var optExpressions = Visit(newExpressions); if (oldVariables.Count == unassigned.Count) { return(block.Update(variables: null, optExpressions)); } else { var newVariables = new ParameterExpression[oldVariables.Count - unassigned.Count]; for (int i = 0, j = 0, n = oldVariables.Count; i < n; i++) { var variable = oldVariables[i]; if (!unassigned.Contains(variable)) { newVariables[j++] = variable; } } return(block.Update(newVariables, optExpressions)); } } } return(block); }
protected override Expression VisitBlock(BlockExpression node) { if (CanOptimize(node)) { return FlattenBlocks(node); } var variables = VisitAndConvert(node.Variables, nameof(VisitBlock)); var expressions = VisitSequence(node.Expressions); return node.Update(variables, expressions); }
/// <summary> /// Removes unused declared variables from the specified block. /// </summary> /// <param name="block">The block to remove unused declared variables from.</param> /// <returns>The result of removing unused variables.</returns> /// <example> /// <code>{ int x, int y; x + 1; }</code> /// becomes /// <code>{ int x; x + 1; }</code> /// </example> private static BlockExpression RemoveUnusedVariables(BlockExpression block) { var variables = block.Variables; var n = variables.Count; if (n > 0) { // REVIEW: This does a lot of repeated scanning of the same child nodes. // We could consider storing these results for later reuse. var finder = new FreeVariableFinder(); finder.Visit(block.Expressions); var freeVariables = finder.FreeVariables; var remainingVariables = default(List <ParameterExpression>); for (var i = 0; i < n; i++) { var variable = variables[i]; if (!freeVariables.Contains(variable)) { if (remainingVariables == null) { remainingVariables = new List <ParameterExpression>(n); for (var j = 0; j < i; j++) { remainingVariables.Add(variables[j]); } } } else { remainingVariables?.Add(variable); } } if (remainingVariables != null) { return(block.Update(remainingVariables, block.Expressions)); } } return(block); }
/// <summary> /// Removes pure statements from the specified <paramref name="block"/> expression. /// </summary> /// <param name="block">The block to remove pure statements from.</param> /// <returns>The result of removing pure statements.</returns> /// <example> /// <code>{ ; x; F(); 42; G(); }</code> /// becomes /// <code>{ F(); G(); }</code> /// </example> private Expression RemovePureStatements(BlockExpression block) { var oldExpressions = block.Expressions; var newExpressions = default(List <Expression>); var count = oldExpressions.Count; for (int i = 0, n = count - 1; i < n; i++) { var expr = block.Expressions[i]; if (IsPure(expr)) { if (newExpressions == null) { newExpressions = new List <Expression>(n); for (var j = 0; j < i; j++) { newExpressions.Add(oldExpressions[j]); } } } else if (newExpressions != null) { newExpressions.Add(expr); } } if (newExpressions == null) { return(block); } else if (newExpressions.Count == 0) { return(ChangeType(oldExpressions[count - 1], block.Type)); } else { newExpressions.Add(oldExpressions[count - 1]); return(block.Update(block.Variables, newExpressions)); } }
protected override Expression VisitBlock(BlockExpression node) { if (node.Variables.Count > 0) { _environment.Push(node.Variables); } // // Expressions execute top-to-bottom. Any branches will be taken care of by visits to Label, Goto, // Conditional, Try, or Loop expressions, so we can just proceed in order. // var expressions = Visit(node.Expressions); if (node.Variables.Count > 0) { _environment.Pop(); } return(node.Update(node.Variables, expressions)); }
protected override Expression VisitBlock(BlockExpression node) { var exprs = node.Expressions.SelectMany(e => new Expression[] { _log(Expression.Constant("S" + _n++)), Visit(e) }).ToList(); return node.Update(node.Variables, exprs); }
/// <summary> /// Flattens nested blocks to reduce the nesting level. /// </summary> /// <param name="block">The block to flatten.</param> /// <returns>The result of flattening nested blocks.</returns> /// <example> /// <code>{ int x; { int y; x + y; } }</code> /// becomes /// <code>{ int x, int y; x + y; }</code> /// </example> private Expression FlattenBlocks(BlockExpression block) { var allVariables = default(HashSet <ParameterExpression>); var variableList = default(List <ParameterExpression>); var current = block; // // NB: This optimization is only safe for blocks with single expressions in // them because we're flattening declaration scopes. In a block has more // than one expression, this could result in changing bindings: // // { int x; { int y; x + y; } y; } -> { int x, int y; x + y; y } // // In this example, the binding of expression `y` in the outer block has // changed due to scope merging. If only a single child block expression // exists, this can't occur. // // CONSIDER: There are more cases where flattening blocks is safe to do, but // these may require more analysis steps. Nested blocks without any // variables or local branches could be merged into the parent. // while (current.Expressions.Count == 1 && current.Expressions[0].NodeType == ExpressionType.Block) { var variables = current.Variables; if (variables.Count > 0) { if (allVariables == null) { allVariables = new HashSet <ParameterExpression>(); variableList = new List <ParameterExpression>(); } foreach (var variable in variables) { if (allVariables.Add(variable)) { variableList.Add(variable); } } } current = (BlockExpression)current.Expressions[0]; } if (current != block) { IEnumerable <ParameterExpression> variables; if (variableList == null) { variables = current.Variables; } else { if (current.Variables.Count > 0) { foreach (var variable in current.Variables) { if (allVariables.Add(variable)) { variableList.Add(variable); } } } variables = variableList; } var expressions = current.Expressions; block = block.Update(variables, expressions); } if (block.Variables.Count == 0 && block.Expressions.Count == 1) { return(ChangeType(block.Expressions[0], block.Type)); } return(block); }
private static BlockExpression Update(BlockExpression node) { // Tests the call of Update to Expression.Block factories. var res = node.Update(node.Variables, node.Expressions.ToArray()); Assert.NotSame(node, res); return res; }