List <Cmd> SliceCmds(Block b) { Contract.Requires(b != null); Contract.Ensures(Contract.Result <List <Cmd> >() != null); List <Cmd> seq = b.Cmds; Contract.Assert(seq != null); if (!doingSlice && !ShouldAssumize(b)) { return(seq); } List <Cmd> res = new List <Cmd>(); foreach (Cmd c in seq) { Contract.Assert(c != null); AssertCmd a = c as AssertCmd; Cmd theNewCmd = c; bool swap = false; if (a != null) { if (doingSlice) { double cost = AssertionCost(a); bool first = (sliceLimit - cost) >= 0 || sliceInitialLimit == sliceLimit; sliceLimit -= cost; swap = slicePos == first; } else if (assertToAssume) { swap = true; } else { Contract.Assert(false); throw new cce.UnreachableException(); } if (swap) { theNewCmd = VCGen.AssertTurnedIntoAssume(a); } } res.Add(theNewCmd); } return(res); }
/// <summary> /// Starting from the 0-index "split_here" annotation in begin, verifies until it reaches a subsequent "split_here" annotation /// Returns a list of blocks where all code not verified has asserts converted into assumes /// </summary> /// <param name="blocks">Implementation's collection of blocks</param> /// <param name="begin">Block containing the first split_here from which to start verifying</param> /// <param name="beginSplitId">0-based ID of the "split_here" annotation within begin at which to start verifying</param> /// <param name="blockInternalSplit">True if the entire split is contained within block begin</param> /// <param name="endPoints">Set of all blocks containing a "split_here" annotation</param> /// <returns></returns> // Note: Current implementation may over report errors. // For example, if the control flow graph is a diamond (e.g., A -> B, C, B->D, C->D), // and there is a split in B and an error in D, then D will be verified twice and hence report the error twice. // Best solution may be to memoize blocks that have been fully verified and be sure not to verify them again private static List <Block> DoManualSplit(List <Block> blocks, Block begin, int beginSplitId, bool blockInternalSplit, IEnumerable <Block> endPoints) { // Compute the set of blocks reachable from begin but not included in endPoints. These will be verified in their entirety. var blocksToVerifyEntirely = new HashSet <Block>(); var reachableEndPoints = new HashSet <Block>(); // Reachable end points will be verified up to their first split point var todo = new Stack <Block>(); todo.Push(begin); while (todo.Count > 0) { var currentBlock = todo.Pop(); if (blocksToVerifyEntirely.Contains(currentBlock)) { continue; } blocksToVerifyEntirely.Add(currentBlock); var exit = currentBlock.TransferCmd as GotoCmd; if (exit != null) { foreach (Block targetBlock in exit.labelTargets) { if (!endPoints.Contains(targetBlock)) { todo.Push(targetBlock); } else { reachableEndPoints.Add(targetBlock); } } } } blocksToVerifyEntirely.Remove(begin); // Convert assumes to asserts in "unreachable" blocks, including portions of blocks containing "split_here" var newBlocks = new List <Block>(blocks.Count()); // Copies of the original blocks var duplicator = new Duplicator(); var oldToNewBlockMap = new Dictionary <Block, Block>(blocks.Count()); // Maps original blocks to their new copies in newBlocks foreach (var currentBlock in blocks) { var newBlock = (Block)duplicator.VisitBlock(currentBlock); oldToNewBlockMap[currentBlock] = newBlock; newBlocks.Add(newBlock); if (!blockInternalSplit && blocksToVerifyEntirely.Contains(currentBlock)) { continue; // All reachable blocks must be checked in their entirety, so don't change anything } // Otherwise, we only verify a portion of the current block, so we'll need to look at each of its commands // !verify -> convert assert to assume var verify = (currentBlock == begin && beginSplitId == -1 ) || // -1 tells us to start verifying from the very beginning (i.e., there is no split in the begin block) ( reachableEndPoints .Contains(currentBlock) && // This endpoint is reachable from begin, so we verify until we hit the first split point !blockInternalSplit); // Don't bother verifying if all of the splitting is within the begin block var newCmds = new List <Cmd>(); var splitHereCount = 0; foreach (Cmd c in currentBlock.Cmds) { var p = c as PredicateCmd; if (p != null && QKeyValue.FindBoolAttribute(p.Attributes, "split_here")) { if (currentBlock == begin) { // Verify everything between the beginSplitId we were given and the next split if (splitHereCount == beginSplitId) { verify = true; } else if (splitHereCount == beginSplitId + 1) { verify = false; } } else { // We're in an endpoint so we stop verifying as soon as we hit a "split_here" verify = false; } splitHereCount++; } var asrt = c as AssertCmd; if (verify || asrt == null) { newCmds.Add(c); } else { newCmds.Add(VCGen.AssertTurnedIntoAssume(asrt)); } } newBlock.Cmds = newCmds; } // Patch the edges between the new blocks foreach (var oldBlock in blocks) { if (oldBlock.TransferCmd is ReturnCmd) { continue; } var gotoCmd = (GotoCmd)oldBlock.TransferCmd; var newLabelTargets = new List <Block>(gotoCmd.labelTargets.Count()); var newLabelNames = new List <string>(gotoCmd.labelTargets.Count()); foreach (var target in gotoCmd.labelTargets) { newLabelTargets.Add(oldToNewBlockMap[target]); newLabelNames.Add(oldToNewBlockMap[target].Label); } oldToNewBlockMap[oldBlock].TransferCmd = new GotoCmd(gotoCmd.tok, newLabelNames, newLabelTargets); } return(newBlocks); }