/// <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); }
public static VerificationResult VerifyImplementation(VC.ConditionGeneration vcgen, Implementation impl, Program prog, out SDiffCounterexamples cex, out List <Model> errModelList) { VerifyImplCleanup vic; vic = new VerifyImplCleanup(); vic.Visit(prog); cex = null; errModelList = null; if (impl == null) { Log.Out(Log.Urgent, "VerifyImplementation saw null implementation"); return(VerificationResult.Unknown); } //Log.Out(Log.Verifier, "Verifying implementation " + impl.Name); List <Counterexample> errors; List <Model> errorsModel; VerificationResult sdoutcome = VerificationResult.Unknown; VC.VCGen.Outcome outcome; //Log.Out(Log.Verifier, "Saving implementation before Boogie preprocessing"); var duper = new Duplicator(); var imperativeBlocks = new Dictionary <string, Block>(); foreach (Block b in impl.Blocks) { //new: to avoid repeated blocks (MYSTERY) if (!imperativeBlocks.ContainsKey(b.Label)) { imperativeBlocks.Add(b.Label, duper.VisitBlock(b)); } } try { var start = DateTime.Now; //outcome = vcgen.VerifyImplementation(impl, prog, out errors); outcome = vcgen.VerifyImplementation(impl, /*prog,*/ out errors, out errorsModel); errModelList = errorsModel; var end = DateTime.Now; TimeSpan elapsed = end - start; Console.WriteLine(string.Format(" [{0} s] ", elapsed.TotalSeconds)); } catch (VC.VCGenException e) { Log.Out(Log.Error, "Error BP5010: {0} Encountered in implementation {1}: " + e.Message); errors = null; outcome = VC.VCGen.Outcome.Inconclusive; } catch (UnexpectedProverOutputException upo) { Log.Out(Log.Error, "Advisory: {0} SKIPPED because of internal error: unexpected prover output: {1}" + upo.Message); errors = null; outcome = VC.VCGen.Outcome.Inconclusive; } catch (Exception e) { Log.Out(Log.Error, "Unknown error somewhere in verification: "); Log.Out(Log.Error, e.ToString()); return(VerificationResult.Unknown); } switch (outcome) { case VC.VCGen.Outcome.Correct: sdoutcome = VerificationResult.Verified; break; case VC.VCGen.Outcome.Errors: sdoutcome = VerificationResult.Error; break; case VC.VCGen.Outcome.Inconclusive: sdoutcome = VerificationResult.Inconclusive; break; case VC.VCGen.Outcome.OutOfMemory: sdoutcome = VerificationResult.OutOfMemory; break; case VC.VCGen.Outcome.TimedOut: sdoutcome = VerificationResult.TimeOut; break; } Log.Out(Log.Normal, outcome.ToString()); var eqVarName = ""; if (errors != null && errors.Count() == 1) { //eqVarName = errors[0]; } Log.Out(Log.Verifier, (errors == null ? 0 : errors.Count) + " counterexamples..."); if (errors != null) { cex = new SDiffCounterexamples(); for (int i = 0; i < errors.Count; i++) { if (Options.EnumerateAllPaths) { //just remove any time for this option cex.Add(new SDiffCounterexample(errors[i], null, impl)); continue; } //reconstruct trace in terms of imperative blocks var trace = ReconstructImperativeTrace(errors[i].Trace, imperativeBlocks); if (SymEx.TraceValidator.Validate(trace)) { Log.Out(Log.Cex, "Trace " + "[" + i + "]:"); Log.Out(Log.Cex, "Validating..."); Log.Out(Log.Cex, "Trace is not complete! Printing.."); SDiff.SymEx.CexDumper.PrintTrace(trace); continue; } else { //Log.Out(Log.Cex, "Trace OK"); if (Options.DumpValidTraces) { SDiff.SymEx.CexDumper.PrintTrace(trace); } } cex.Add(new SDiffCounterexample(errors[i], trace, impl)); } } return(sdoutcome); }