//----------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All Rights Reserved. // //----------------------------------------------------------------------------- namespace Microsoft.Boogie { using System; using System.Collections.Generic; using System.Diagnostics.Contracts; /// /// This class provides the functionality of traversing a program to determine which /// blocks are blocks where the widening operator may need to be applied. Assumes /// all 'currentlyTraversed' bits to be initially false, and leaves them that way in /// the end. Assumes the 'widenBlock' bits are initially false, and sets them /// appropriately. /// public class WidenPoints { /// /// Compute the widen points of a program /// public static void Compute(Program program) { Contract.Requires(program != null); cce.BeginExpose(program); foreach (var impl in program.Implementations) { if (impl.Blocks != null && impl.Blocks.Count > 0) { Contract.Assume(cce.IsConsistent(impl)); cce.BeginExpose(impl); Block start = impl.Blocks[0]; Contract.Assume(start != null); Contract.Assume(cce.IsConsistent(start)); Visit(start); // We reset the state... foreach (Block b in impl.Blocks) { cce.BeginExpose(b); b.TraversingStatus = Block.VisitState.ToVisit; cce.EndExpose(); } cce.EndExpose(); } } cce.EndExpose(); } static void Visit(Block b) { Contract.Requires(b != null); Contract.Assume(cce.IsExposable(b)); if (b.TraversingStatus == Block.VisitState.BeingVisited) { cce.BeginExpose(b); // we got here through a back-edge b.widenBlock = true; cce.EndExpose(); } else if (b.TraversingStatus == Block.VisitState.AlreadyVisited) { // do nothing... we already saw this node } else if (b.TransferCmd is GotoCmd) { Contract.Assert(b.TraversingStatus == Block.VisitState.ToVisit); GotoCmd g = (GotoCmd)b.TransferCmd; cce.BeginExpose(b); cce.BeginExpose(g); //PM: required for the subsequent expose (g.labelTargets) b.TraversingStatus = Block.VisitState.BeingVisited; // labelTargets is made non-null by Resolve, which we assume // has already called in a prior pass. Contract.Assume(g.labelTargets != null); cce.BeginExpose(g.labelTargets); foreach (Block succ in g.labelTargets) // invariant b.currentlyTraversed; //PM: The following loop invariant will work once properties are axiomatized //&& (g.labelNames != null && g.labelTargets != null ==> g.labelNames.Length == g.labelTargets.Length); { Contract.Assert(succ != null); Visit(succ); } cce.EndExpose(); Contract.Assert(b.TraversingStatus == Block.VisitState.BeingVisited); // System.Diagnostics.Debug.Assert(b.currentlyTraversed); b.TraversingStatus = Block.VisitState.AlreadyVisited; //PM: The folowing assumption is needed because we cannot prove that a simple field update //PM: leaves the value of a property unchanged. Contract.Assume(g.labelNames == null || g.labelNames.Count == g.labelTargets.Count); cce.EndExpose(); } else { Contract.Assert(b.TransferCmd == null || b.TransferCmd is ReturnCmd); // It must be a returnCmd; } } static private Block rootBlock = null; // The root point we have to consider /// /// Compute the blocks in the body loop. /// Tt is the head of the loop. It must be a widen block /// The blocks that are in the loop from block /// public static List ComputeLoopBodyFrom(Block block) { Contract.Requires(block.widenBlock); Contract.Requires(block != null); Contract.Ensures(cce.NonNullElements(Contract.Result>())); Contract.Assert(rootBlock == null); rootBlock = block; List blocksInLoop = new List(); // We use a list just because .net does not define a set List visitingPath = new List(); // The order is important, as we want paths blocksInLoop.Add(block); DoDFSVisit(block, visitingPath, blocksInLoop); visitingPath.Add(block); rootBlock = null; // We reset the invariant return blocksInLoop; } /// /// Perform the Depth-first search of the so to find the loop /// The block to visit /// The path we are visiting so far /// private static void DoDFSVisit(Block block, List path, List blocksInPath) { Contract.Requires(block != null); Contract.Requires(cce.NonNullElements(path)); Contract.Requires(cce.NonNullElements(path)); #region case 1. We visit the root => We are done, "path" is a path inside the loop if (block == rootBlock && path.Count > 1) { blocksInPath.AddRange(path); // Add all the blocks in this path } #endregion #region case 2. We visit a node that ends with a return => "path" is not inside the loop if (block.TransferCmd is ReturnCmd) { return; } #endregion #region case 3. We visit a node with successors => continue the exploration of its successors { Contract.Assert(block.TransferCmd is GotoCmd); GotoCmd successors = (GotoCmd)block.TransferCmd; Contract.Assert(successors != null); if (successors.labelTargets != null) foreach (Block nextBlock in successors.labelTargets) { Contract.Assert(nextBlock != null); if (path.Contains(nextBlock)) // If the current path has already seen the block, just skip it continue; // Otherwise we perform the DFS visit path.Add(nextBlock); DoDFSVisit(nextBlock, path, blocksInPath); Contract.Assert(nextBlock == path[path.Count - 1]); path.RemoveAt(path.Count - 1); } } #endregion } } }