//----------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All Rights Reserved. // //----------------------------------------------------------------------------- namespace Microsoft.Boogie { using System; using System.Collections.Generic; using Microsoft.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) { expose (program) { foreach (Declaration m in program.TopLevelDeclarations) { Implementation impl = m as Implementation; if (impl != null) { if (impl.Blocks != null && impl.Blocks.Count > 0) { assume impl.IsConsistent; expose (impl) { Block start = impl.Blocks[0]; assume start != null; assume start.IsConsistent; Visit(start); // We reset the state... foreach(Block b in impl.Blocks) { expose (b) { b.TraversingStatus = Block.VisitState.ToVisit; } } } } } } } } static void Visit(Block! b) { assume b.IsExposable; if (b.TraversingStatus == Block.VisitState.BeingVisited) { expose (b) { // we got here through a back-edge b.widenBlock = true; } } else if(b.TraversingStatus == Block.VisitState.AlreadyVisited) { // do nothing... we already saw this node } else if (b.TransferCmd is GotoCmd) { assert b.TraversingStatus == Block.VisitState.ToVisit; GotoCmd g = (GotoCmd)b.TransferCmd; expose (b) { expose (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. assume g.labelTargets != null; expose (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); { assert succ != null; Visit(succ); } } 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. assume (g.labelNames != null ==> g.labelNames.Length == g.labelTargets.Length); } } } else { 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) requires block.widenBlock; { 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) { #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 { assert block.TransferCmd is GotoCmd; GotoCmd! successors = (GotoCmd) block.TransferCmd; if (successors.labelTargets != null) foreach (Block! nextBlock in successors.labelTargets) { 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); assert nextBlock == path[path.Count-1]; path.RemoveAt(path.Count-1); } } #endregion } } }