diff options
Diffstat (limited to 'Source/VCGeneration/VC.cs')
-rw-r--r-- | Source/VCGeneration/VC.cs | 8027 |
1 files changed, 4134 insertions, 3893 deletions
diff --git a/Source/VCGeneration/VC.cs b/Source/VCGeneration/VC.cs index 560f55b4..6e43e917 100644 --- a/Source/VCGeneration/VC.cs +++ b/Source/VCGeneration/VC.cs @@ -1,3893 +1,4134 @@ -//-----------------------------------------------------------------------------
-//
-// Copyright (C) Microsoft Corporation. All Rights Reserved.
-//
-//-----------------------------------------------------------------------------
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-using System.Threading;
-using System.IO;
-using Microsoft.Boogie;
-using Microsoft.Boogie.GraphUtil;
-using System.Diagnostics.Contracts;
-using Microsoft.Basetypes;
-using Microsoft.Boogie.VCExprAST;
-
-namespace VC {
- using Bpl = Microsoft.Boogie;
- using System.Threading.Tasks;
-
- public class VCGen : ConditionGeneration {
- private const bool _print_time = false;
- /// <summary>
- /// Constructor. Initializes the theorem prover.
- /// </summary>
- [NotDelayed]
- public VCGen(Program program, string/*?*/ logFilePath, bool appendLogFile, List<Checker> checkers)
- : base(program, checkers)
- {
- Contract.Requires(program != null);
- this.appendLogFile = appendLogFile;
- this.logFilePath = logFilePath;
- }
-
- private static AssumeCmd AssertTurnedIntoAssume(AssertCmd assrt) {
- Contract.Requires(assrt != null);
- Contract.Ensures(Contract.Result<AssumeCmd>() != null);
-
- Expr expr = assrt.Expr;
- Contract.Assert(expr != null);
- switch (Wlp.Subsumption(assrt)) {
- case CommandLineOptions.SubsumptionOption.Never:
- expr = Expr.True;
- break;
- case CommandLineOptions.SubsumptionOption.Always:
- break;
- case CommandLineOptions.SubsumptionOption.NotForQuantifiers:
- if (expr is QuantifierExpr) {
- expr = Expr.True;
- }
- break;
- default:
- Contract.Assert(false);
- throw new cce.UnreachableException(); // unexpected case
- }
-
- return new AssumeCmd(assrt.tok, expr);
- }
-
- #region Soundness smoke tester
- class SmokeTester {
- [ContractInvariantMethod]
- void ObjectInvariant() {
- Contract.Invariant(parent != null);
- Contract.Invariant(impl != null);
- Contract.Invariant(initial != null);
- Contract.Invariant(cce.NonNullDictionaryAndValues(copies));
- Contract.Invariant(cce.NonNull(visited));
- Contract.Invariant(callback != null);
- }
-
- VCGen parent;
- Implementation impl;
- Block initial;
- int id;
- Dictionary<Block, Block> copies = new Dictionary<Block, Block>();
- HashSet<Block> visited = new HashSet<Block>();
- VerifierCallback callback;
-
- internal SmokeTester(VCGen par, Implementation i, VerifierCallback callback) {
- Contract.Requires(par != null);
- Contract.Requires(i != null);
- Contract.Requires(callback != null);
- parent = par;
- impl = i;
- initial = i.Blocks[0];
- this.callback = callback;
- }
-
- internal void Copy() {
- CloneBlock(impl.Blocks[0]);
- initial = GetCopiedBlocks()[0];
- }
-
- internal void Test() {
- Contract.EnsuresOnThrow<UnexpectedProverOutputException>(true);
-
- DFS(initial);
- }
-
- void TopologicalSortImpl() {
- Graph<Block> dag = new Graph<Block>();
- dag.AddSource(cce.NonNull(impl.Blocks[0])); // there is always at least one node in the graph
- foreach (Block b in impl.Blocks) {
- GotoCmd gtc = b.TransferCmd as GotoCmd;
- if (gtc != null) {
- Contract.Assume(gtc.labelTargets != null);
- foreach (Block dest in gtc.labelTargets) {
- Contract.Assert(dest != null);
- dag.AddEdge(b, dest);
- }
- }
- }
- impl.Blocks = new List<Block>();
- foreach (Block b in dag.TopologicalSort()) {
- Contract.Assert(b != null);
- impl.Blocks.Add(b);
- }
- }
-
- void Emit() {
- TopologicalSortImpl();
- EmitImpl(impl, false);
- }
-
- // this one copies forward
- Block CloneBlock(Block b) {
- Contract.Requires(b != null);
- Contract.Ensures(Contract.Result<Block>() != null);
-
- Block fake_res;
- if (copies.TryGetValue(b, out fake_res)) {
- return cce.NonNull(fake_res);
- }
- Block res = new Block(b.tok, b.Label, new List<Cmd>(b.Cmds), null);
- copies[b] = res;
- if (b.TransferCmd is GotoCmd) {
- foreach (Block ch in cce.NonNull((GotoCmd)b.TransferCmd).labelTargets) {
- Contract.Assert(ch != null);
- CloneBlock(ch);
- }
- }
- foreach (Block p in b.Predecessors) {
- Contract.Assert(p != null);
- res.Predecessors.Add(CloneBlock(p));
- }
- return res;
- }
-
- // this one copies backwards
- Block CopyBlock(Block b) {
- Contract.Requires(b != null);
- Contract.Ensures(Contract.Result<Block>() != null);
-
- Block fake_res;
- if (copies.TryGetValue(b, out fake_res)) {
- // fake_res should be Block! but the compiler fails
- return cce.NonNull(fake_res);
- }
- Block res;
- List<Cmd> seq = new List<Cmd>();
- foreach (Cmd c in b.Cmds) {
- Contract.Assert(c != null);
- AssertCmd turn = c as AssertCmd;
- if (!turnAssertIntoAssumes || turn == null) {
- seq.Add(c);
- } else {
- seq.Add(AssertTurnedIntoAssume(turn));
- }
- }
- res = new Block(b.tok, b.Label, seq, null);
- copies[b] = res;
- foreach (Block p in b.Predecessors) {
- Contract.Assert(p != null);
- res.Predecessors.Add(CopyBlock(p));
- }
- return res;
- }
-
- List<Block> GetCopiedBlocks() {
- Contract.Ensures(cce.NonNullElements(Contract.Result<List<Block>>()));
-
- // the order of nodes in res is random (except for the first one, being the entry)
- List<Block> res = new List<Block>();
- res.Add(copies[initial]);
-
- foreach (KeyValuePair<Block, Block> kv in copies) {
- Contract.Assert(kv.Key != null&&kv.Value!=null);
- GotoCmd go = kv.Key.TransferCmd as GotoCmd;
- ReturnCmd ret = kv.Key.TransferCmd as ReturnCmd;
- if (kv.Key != initial) {
- res.Add(kv.Value);
- }
- if (go != null) {
- GotoCmd copy = new GotoCmd(go.tok, new List<String>(), new List<Block>());
- kv.Value.TransferCmd = copy;
- foreach (Block b in cce.NonNull(go.labelTargets)) {
- Contract.Assert(b != null);
- Block c;
- if (copies.TryGetValue(b, out c)) {
- copy.AddTarget(cce.NonNull(c));
- }
- }
- } else if (ret != null) {
- kv.Value.TransferCmd = ret;
- } else {
- Contract.Assume(false);
- throw new cce.UnreachableException();
- }
- }
-
- copies.Clear();
-
- return res;
- }
-
- // check if e is true, false, !true, !false
- // if so return true and the value of the expression in val
- bool BooleanEval(Expr e, ref bool val) {
- Contract.Requires(e != null);
- LiteralExpr lit = e as LiteralExpr;
- NAryExpr call = e as NAryExpr;
-
- if (lit != null && lit.isBool) {
- val = lit.asBool;
- return true;
- } else if (call != null &&
- call.Fun is UnaryOperator &&
- ((UnaryOperator)call.Fun).Op == UnaryOperator.Opcode.Not &&
- BooleanEval(cce.NonNull(call.Args[0]), ref val)) {
- val = !val;
- return true;
- }
- // this is for the 0bv32 != 0bv32 generated by vcc
- else if (call != null &&
- call.Fun is BinaryOperator &&
- ((BinaryOperator)call.Fun).Op == BinaryOperator.Opcode.Neq &&
- call.Args[0] is LiteralExpr &&
- cce.NonNull(call.Args[0]).Equals(call.Args[1])) {
- val = false;
- return true;
- }
-
- return false;
- }
-
- bool IsFalse(Expr e) {
- Contract.Requires(e != null);
- bool val = false;
- return BooleanEval(e, ref val) && !val;
- }
-
- bool CheckUnreachable(Block cur, List<Cmd> seq)
- {
- Contract.Requires(cur != null);
- Contract.Requires(seq != null);
- Contract.EnsuresOnThrow<UnexpectedProverOutputException>(true);
- foreach (Cmd cmd in seq)
- {
- AssertCmd assrt = cmd as AssertCmd;
- if (assrt != null && QKeyValue.FindBoolAttribute(assrt.Attributes, "PossiblyUnreachable"))
- return false;
- }
-
- DateTime start = DateTime.UtcNow;
- if (CommandLineOptions.Clo.Trace)
- {
- System.Console.Write(" soundness smoke test #{0} ... ", id);
- }
- callback.OnProgress("smoke", id, id, 0.0);
-
- Token tok = new Token();
- tok.val = "soundness smoke test assertion";
- seq.Add(new AssertCmd(tok, Expr.False));
- Block copy = CopyBlock(cur);
- Contract.Assert(copy != null);
- copy.Cmds = seq;
- List<Block> backup = impl.Blocks;
- Contract.Assert(backup != null);
- impl.Blocks = GetCopiedBlocks();
- copy.TransferCmd = new ReturnCmd(Token.NoToken);
- if (CommandLineOptions.Clo.TraceVerify)
- {
- System.Console.WriteLine();
- System.Console.WriteLine(" --- smoke #{0}, before passify", id);
- Emit();
- }
- parent.CurrentLocalVariables = impl.LocVars;
- ModelViewInfo mvInfo;
- parent.PassifyImpl(impl, out mvInfo);
- Dictionary<int, Absy> label2Absy;
- Checker ch = parent.FindCheckerFor(CommandLineOptions.Clo.SmokeTimeout);
- Contract.Assert(ch != null);
-
- ProverInterface.Outcome outcome = ProverInterface.Outcome.Undetermined;
- try
- {
- lock (ch)
- {
- var exprGen = ch.TheoremProver.Context.ExprGen;
- VCExpr controlFlowVariableExpr = CommandLineOptions.Clo.UseLabels ? null : exprGen.Integer(BigNum.ZERO);
-
- VCExpr vc = parent.GenerateVC(impl, controlFlowVariableExpr, out label2Absy, ch.TheoremProver.Context);
- Contract.Assert(vc != null);
-
- if (!CommandLineOptions.Clo.UseLabels)
- {
- VCExpr controlFlowFunctionAppl = exprGen.ControlFlowFunctionApplication(exprGen.Integer(BigNum.ZERO), exprGen.Integer(BigNum.ZERO));
- VCExpr eqExpr = exprGen.Eq(controlFlowFunctionAppl, exprGen.Integer(BigNum.FromInt(impl.Blocks[0].UniqueId)));
- vc = exprGen.Implies(eqExpr, vc);
- }
-
- impl.Blocks = backup;
-
- if (CommandLineOptions.Clo.TraceVerify)
- {
- System.Console.WriteLine(" --- smoke #{0}, after passify", id);
- Emit();
- }
-
- ch.BeginCheck(cce.NonNull(impl.Name + "_smoke" + id++), vc, new ErrorHandler(label2Absy, this.callback));
- }
-
- ch.ProverTask.Wait();
-
- lock (ch)
- {
- outcome = ch.ReadOutcome();
- }
- }
- finally
- {
- ch.GoBackToIdle();
- }
-
- parent.CurrentLocalVariables = null;
-
- DateTime end = DateTime.UtcNow;
- TimeSpan elapsed = end - start;
- if (CommandLineOptions.Clo.Trace)
- {
- System.Console.WriteLine(" [{0} s] {1}", elapsed.TotalSeconds,
- outcome == ProverInterface.Outcome.Valid ? "OOPS" :
- "OK" + (outcome == ProverInterface.Outcome.Invalid ? "" : " (" + outcome + ")"));
- }
-
- if (outcome == ProverInterface.Outcome.Valid)
- {
- // copy it again, so we get the version with calls, assignments and such
- copy = CopyBlock(cur);
- copy.Cmds = seq;
- impl.Blocks = GetCopiedBlocks();
- TopologicalSortImpl();
- callback.OnUnreachableCode(impl);
- impl.Blocks = backup;
- return true;
- }
- return false;
- }
-
- const bool turnAssertIntoAssumes = false;
-
- void DFS(Block cur) {
- Contract.Requires(cur != null);
- Contract.EnsuresOnThrow<UnexpectedProverOutputException>(true);
- if (visited.Contains(cur))
- return;
- visited.Add(cur);
-
- List<Cmd> seq = new List<Cmd>();
- foreach (Cmd cmd_ in cur.Cmds) {
- Cmd cmd = cmd_;
- Contract.Assert(cmd != null);
- AssertCmd assrt = cmd as AssertCmd;
- AssumeCmd assm = cmd as AssumeCmd;
- CallCmd call = cmd as CallCmd;
-
- bool assumeFalse = false;
-
- if (assrt != null) {
- // we're not going any further
- // it's clear the user expected unreachable code here
- // it's not clear where did he expect it, maybe it would be right to insert
- // a check just one command before
- if (IsFalse(assrt.Expr))
- return;
-
-#if TURN_ASSERT_INFO_ASSUMES
- if (turnAssertIntoAssumes) {
- cmd = AssertTurnedIntoAssume(assrt);
- }
-#endif
- } else if (assm != null) {
- if (IsFalse(assm.Expr))
- assumeFalse = true;
- } else if (call != null) {
- foreach (Ensures e in (cce.NonNull(call.Proc)).Ensures) {
- Contract.Assert(e != null);
- if (IsFalse(e.Condition))
- assumeFalse = true;
- }
- }
-
- if (assumeFalse) {
- CheckUnreachable(cur, seq);
- return;
- }
-
- seq.Add(cmd);
- }
-
-
- GotoCmd go = cur.TransferCmd as GotoCmd;
- ReturnCmd ret = cur.TransferCmd as ReturnCmd;
-
- Contract.Assume(!(go != null && go.labelTargets == null && go.labelNames != null && go.labelNames.Count > 0));
-
- if (ret != null || (go != null && cce.NonNull(go.labelTargets).Count == 0)) {
- // we end in return, so there will be no more places to check
- CheckUnreachable(cur, seq);
- } else if (go != null) {
- bool needToCheck = true;
- // if all of our children have more than one parent, then
- // we're in the right place to check
- foreach (Block target in cce.NonNull(go.labelTargets)) {
- Contract.Assert(target != null);
- if (target.Predecessors.Count == 1) {
- needToCheck = false;
- }
- }
- if (needToCheck) {
- CheckUnreachable(cur, seq);
- }
- foreach (Block target in go.labelTargets) {
- Contract.Assert(target != null);
- DFS(target);
- }
- }
- }
-
- class ErrorHandler : ProverInterface.ErrorHandler {
- Dictionary<int, Absy> label2Absy;
- VerifierCallback callback;
- [ContractInvariantMethod]
- void ObjectInvariant() {
- Contract.Invariant(label2Absy != null);
- Contract.Invariant(callback != null);
- }
-
-
- public ErrorHandler(Dictionary<int, Absy> label2Absy, VerifierCallback callback) {
- Contract.Requires(label2Absy != null);
- Contract.Requires(callback != null);
- this.label2Absy = label2Absy;
- this.callback = callback;
- }
-
- public override Absy Label2Absy(string label) {
- //Contract.Requires(label != null);
- Contract.Ensures(Contract.Result<Absy>() != null);
-
- int id = int.Parse(label);
- return cce.NonNull((Absy)label2Absy[id]);
- }
-
- public override void OnProverWarning(string msg) {
- //Contract.Requires(msg != null);
- this.callback.OnWarning(msg);
- }
- }
- }
-
-
- #endregion
-
- #region Splitter
- class Split {
- class BlockStats {
- public bool big_block;
- public int id;
- public double assertion_cost;
- public double assumption_cost; // before multiplier
- public double incomming_paths;
- public List<Block>/*!>!*/ virtual_successors = new List<Block>();
- public List<Block>/*!>!*/ virtual_predecesors = new List<Block>();
- public HashSet<Block> reachable_blocks;
- public readonly Block block;
- [ContractInvariantMethod]
- void ObjectInvariant() {
- Contract.Invariant(cce.NonNullElements(virtual_successors));
- Contract.Invariant(cce.NonNullElements(virtual_predecesors));
- Contract.Invariant(block != null);
- }
-
-
- public BlockStats(Block b, int i) {
- Contract.Requires(b != null);
- block = b;
- assertion_cost = -1;
- id = i;
- }
- }
- [ContractInvariantMethod]
- void ObjectInvariant() {
- Contract.Invariant(cce.NonNullElements(blocks));
- Contract.Invariant(cce.NonNullElements(big_blocks));
- Contract.Invariant(cce.NonNullDictionaryAndValues(stats));
- Contract.Invariant(cce.NonNullElements(assumized_branches));
- Contract.Invariant(gotoCmdOrigins != null);
- Contract.Invariant(parent != null);
- Contract.Invariant(impl != null);
- Contract.Invariant(copies != null);
- Contract.Invariant(cce.NonNull(protected_from_assert_to_assume));
- Contract.Invariant(cce.NonNull(keep_at_all));
- }
-
-
- readonly List<Block> blocks;
- readonly List<Block> big_blocks = new List<Block>();
- readonly Dictionary<Block/*!*/, BlockStats/*!*/>/*!*/ stats = new Dictionary<Block/*!*/, BlockStats/*!*/>();
- readonly int id;
- static int current_id = -1;
- Block split_block;
- bool assert_to_assume;
- List<Block/*!*/>/*!*/ assumized_branches = new List<Block/*!*/>();
-
- double score;
- bool score_computed;
- double total_cost;
- int assertion_count;
- double assertion_cost; // without multiplication by paths
- Dictionary<TransferCmd, ReturnCmd>/*!*/ gotoCmdOrigins;
- readonly public VCGen/*!*/ parent;
- Implementation/*!*/ impl;
-
- Dictionary<Block/*!*/, Block/*!*/>/*!*/ copies = new Dictionary<Block/*!*/, Block/*!*/>();
- bool doing_slice;
- double slice_initial_limit;
- double slice_limit;
- bool slice_pos;
- HashSet<Block/*!*/>/*!*/ protected_from_assert_to_assume = new HashSet<Block/*!*/>();
- HashSet<Block/*!*/>/*!*/ keep_at_all = new HashSet<Block/*!*/>();
-
- // async interface
- private Checker checker;
- private int splitNo;
- internal ErrorReporter reporter;
-
- public Split(List<Block/*!*/>/*!*/ blocks, Dictionary<TransferCmd, ReturnCmd>/*!*/ gotoCmdOrigins, VCGen/*!*/ par, Implementation/*!*/ impl) {
- Contract.Requires(cce.NonNullElements(blocks));
- Contract.Requires(gotoCmdOrigins != null);
- Contract.Requires(par != null);
- Contract.Requires(impl != null);
- this.blocks = blocks;
- this.gotoCmdOrigins = gotoCmdOrigins;
- this.parent = par;
- this.impl = impl;
- this.id = Interlocked.Increment(ref current_id);
- }
-
- public double Cost {
- get {
- ComputeBestSplit();
- return total_cost;
- }
- }
-
- public bool LastChance {
- get {
- ComputeBestSplit();
- return assertion_count == 1 && score < 0;
- }
- }
-
- public string Stats {
- get {
- ComputeBestSplit();
- return string.Format("(cost:{0:0}/{1:0}{2})", total_cost, assertion_cost, LastChance ? " last" : "");
- }
- }
-
- public void DumpDot(int no) {
- using (System.IO.StreamWriter sw = System.IO.File.CreateText(string.Format("split.{0}.dot", no))) {
- sw.WriteLine("digraph G {");
-
- ComputeBestSplit();
- List<Block> saved = assumized_branches;
- Contract.Assert(saved != null);
- assumized_branches = new List<Block>();
- DoComputeScore(false);
- assumized_branches = saved;
-
- foreach (Block b in big_blocks) {
- Contract.Assert(b != null);
- BlockStats s = GetBlockStats(b);
- foreach (Block t in s.virtual_successors) {
- Contract.Assert(t != null);
- sw.WriteLine("n{0} -> n{1};", s.id, GetBlockStats(t).id);
- }
- sw.WriteLine("n{0} [label=\"{1}:\\n({2:0.0}+{3:0.0})*{4:0.0}\"{5}];",
- s.id, b.Label,
- s.assertion_cost, s.assumption_cost, s.incomming_paths,
- s.assertion_cost > 0 ? ",shape=box" : "");
-
- }
- sw.WriteLine("}");
- sw.Close();
- }
-
- string filename = string.Format("split.{0}.bpl", no);
- using (System.IO.StreamWriter sw = System.IO.File.CreateText(filename)) {
- int oldPrintUnstructured = CommandLineOptions.Clo.PrintUnstructured;
- CommandLineOptions.Clo.PrintUnstructured = 2; // print only the unstructured program
- bool oldPrintDesugaringSetting = CommandLineOptions.Clo.PrintDesugarings;
- CommandLineOptions.Clo.PrintDesugarings = false;
- List<Block> backup = impl.Blocks;
- Contract.Assert(backup != null);
- impl.Blocks = blocks;
- impl.Emit(new TokenTextWriter(filename, sw, /*setTokens=*/ false, /*pretty=*/ false), 0);
- impl.Blocks = backup;
- CommandLineOptions.Clo.PrintDesugarings = oldPrintDesugaringSetting;
- CommandLineOptions.Clo.PrintUnstructured = oldPrintUnstructured;
- }
- }
-
- int bsid;
- BlockStats GetBlockStats(Block b) {
- Contract.Requires(b != null);
- Contract.Ensures(Contract.Result<BlockStats>() != null);
-
- BlockStats s;
- if (!stats.TryGetValue(b, out s)) {
- s = new BlockStats(b, bsid++);
- stats[b] = s;
- }
- return cce.NonNull(s);
- }
-
- double AssertionCost(PredicateCmd c) {
- return 1.0;
- }
-
- void CountAssertions(Block b) {
- Contract.Requires(b != null);
- BlockStats s = GetBlockStats(b);
- if (s.assertion_cost >= 0)
- return; // already done
- s.big_block = true;
- s.assertion_cost = 0;
- s.assumption_cost = 0;
- foreach (Cmd c in b.Cmds) {
- if (c is AssertCmd) {
- double cost = AssertionCost((AssertCmd)c);
- s.assertion_cost += cost;
- assertion_count++;
- assertion_cost += cost;
- } else if (c is AssumeCmd) {
- s.assumption_cost += AssertionCost((AssumeCmd)c);
- }
- }
- foreach (Block c in Exits(b)) {
- Contract.Assert(c != null);
- s.virtual_successors.Add(c);
- }
- if (s.virtual_successors.Count == 1) {
- Block next = s.virtual_successors[0];
- BlockStats se = GetBlockStats(next);
- CountAssertions(next);
- if (next.Predecessors.Count > 1 || se.virtual_successors.Count != 1)
- return;
- s.virtual_successors[0] = se.virtual_successors[0];
- s.assertion_cost += se.assertion_cost;
- s.assumption_cost += se.assumption_cost;
- se.big_block = false;
- }
- }
-
- HashSet<Block/*!*/>/*!*/ ComputeReachableNodes(Block/*!*/ b) {
- Contract.Requires(b != null);
- Contract.Ensures(cce.NonNull(Contract.Result<HashSet<Block/*!*/>>()));
- BlockStats s = GetBlockStats(b);
- if (s.reachable_blocks != null) {
- return s.reachable_blocks;
- }
- HashSet<Block/*!*/> blocks = new HashSet<Block/*!*/>();
- s.reachable_blocks = blocks;
- blocks.Add(b);
- foreach (Block/*!*/ succ in Exits(b)) {
- Contract.Assert(succ != null);
- foreach (Block r in ComputeReachableNodes(succ)) {
- Contract.Assert(r != null);
- blocks.Add(r);
- }
- }
- return blocks;
- }
-
- double ProverCost(double vc_cost) {
- return vc_cost * vc_cost;
- }
-
- void ComputeBestSplit() {
- if (score_computed)
- return;
- score_computed = true;
-
- assertion_count = 0;
-
- foreach (Block b in blocks) {
- Contract.Assert(b != null);
- CountAssertions(b);
- }
-
- foreach (Block b in blocks) {
- Contract.Assert(b != null);
- BlockStats bs = GetBlockStats(b);
- if (bs.big_block) {
- big_blocks.Add(b);
- foreach (Block ch in bs.virtual_successors) {
- Contract.Assert(ch != null);
- BlockStats chs = GetBlockStats(ch);
- if (!chs.big_block) {
- Console.WriteLine("non-big {0} accessed from {1}", ch, b);
- DumpDot(-1);
- Contract.Assert(false);
- throw new cce.UnreachableException();
- }
- chs.virtual_predecesors.Add(b);
- }
- }
- }
-
- assumized_branches.Clear();
- total_cost = ProverCost(DoComputeScore(false));
-
- score = double.PositiveInfinity;
- Block best_split = null;
- List<Block> saved_branches = new List<Block>();
-
- foreach (Block b in big_blocks) {
- Contract.Assert(b != null);
- GotoCmd gt = b.TransferCmd as GotoCmd;
- if (gt == null)
- continue;
- List<Block> targ = cce.NonNull(gt.labelTargets);
- if (targ.Count < 2)
- continue;
- // caution, we only consider two first exits
-
- double left0, right0, left1, right1;
- split_block = b;
-
- assumized_branches.Clear();
- assumized_branches.Add(cce.NonNull(targ[0]));
- left0 = DoComputeScore(true);
- right0 = DoComputeScore(false);
-
- assumized_branches.Clear();
- for (int idx = 1; idx < targ.Count; idx++) {
- assumized_branches.Add(cce.NonNull(targ[idx]));
- }
- left1 = DoComputeScore(true);
- right1 = DoComputeScore(false);
-
- double current_score = ProverCost(left1) + ProverCost(right1);
- double other_score = ProverCost(left0) + ProverCost(right0);
-
- if (other_score < current_score) {
- current_score = other_score;
- assumized_branches.Clear();
- assumized_branches.Add(cce.NonNull(targ[0]));
- }
-
- if (current_score < score) {
- score = current_score;
- best_split = split_block;
- saved_branches.Clear();
- saved_branches.AddRange(assumized_branches);
- }
- }
-
- if (CommandLineOptions.Clo.VcsPathSplitMult * score > total_cost) {
- split_block = null;
- score = -1;
- } else {
- assumized_branches = saved_branches;
- split_block = best_split;
- }
- }
-
- void UpdateIncommingPaths(BlockStats s) {
- Contract.Requires(s != null);
- if (s.incomming_paths < 0.0) {
- int count = 0;
- s.incomming_paths = 0.0;
- if (!keep_at_all.Contains(s.block))
- return;
- foreach (Block b in s.virtual_predecesors) {
- Contract.Assert(b != null);
- BlockStats ch = GetBlockStats(b);
- Contract.Assert(ch != null);
- UpdateIncommingPaths(ch);
- if (ch.incomming_paths > 0.0) {
- s.incomming_paths += ch.incomming_paths;
- count++;
- }
- }
- if (count > 1) {
- s.incomming_paths *= CommandLineOptions.Clo.VcsPathJoinMult;
- }
- }
- }
-
- void ComputeBlockSetsHelper(Block b, bool allow_small) {
- Contract.Requires(b != null);
- if (keep_at_all.Contains(b))
- return;
- keep_at_all.Add(b);
-
- if (allow_small) {
- foreach (Block ch in Exits(b)) {
- Contract.Assert(ch != null);
- if (b == split_block && assumized_branches.Contains(ch))
- continue;
- ComputeBlockSetsHelper(ch, allow_small);
- }
- } else {
- foreach (Block ch in GetBlockStats(b).virtual_successors) {
- Contract.Assert(ch != null);
- if (b == split_block && assumized_branches.Contains(ch))
- continue;
- ComputeBlockSetsHelper(ch, allow_small);
- }
- }
- }
-
- void ComputeBlockSets(bool allow_small) {
- protected_from_assert_to_assume.Clear();
- keep_at_all.Clear();
-
- Debug.Assert(split_block == null || GetBlockStats(split_block).big_block);
- Debug.Assert(GetBlockStats(blocks[0]).big_block);
-
- if (assert_to_assume) {
- foreach (Block b in allow_small ? blocks : big_blocks) {
- Contract.Assert(b != null);
- if (ComputeReachableNodes(b).Contains(cce.NonNull(split_block))) {
- keep_at_all.Add(b);
- }
- }
-
- foreach (Block b in assumized_branches) {
- Contract.Assert(b != null);
- foreach (Block r in ComputeReachableNodes(b)) {
- Contract.Assert(r != null);
- if (allow_small || GetBlockStats(r).big_block) {
- keep_at_all.Add(r);
- protected_from_assert_to_assume.Add(r);
- }
- }
- }
- } else {
- ComputeBlockSetsHelper(blocks[0], allow_small);
- }
- }
-
- bool ShouldAssumize(Block b) {
- Contract.Requires(b != null);
- return assert_to_assume && !protected_from_assert_to_assume.Contains(b);
- }
-
- double DoComputeScore(bool aa) {
- assert_to_assume = aa;
- ComputeBlockSets(false);
-
- foreach (Block b in big_blocks) {
- Contract.Assert(b != null);
- GetBlockStats(b).incomming_paths = -1.0;
- }
-
- GetBlockStats(blocks[0]).incomming_paths = 1.0;
-
- double cost = 0.0;
- foreach (Block b in big_blocks) {
- Contract.Assert(b != null);
- if (keep_at_all.Contains(b)) {
- BlockStats s = GetBlockStats(b);
- UpdateIncommingPaths(s);
- double local = s.assertion_cost;
- if (ShouldAssumize(b)) {
- local = (s.assertion_cost + s.assumption_cost) * CommandLineOptions.Clo.VcsAssumeMult;
- } else {
- local = s.assumption_cost * CommandLineOptions.Clo.VcsAssumeMult + s.assertion_cost;
- }
- local = local + local * s.incomming_paths * CommandLineOptions.Clo.VcsPathCostMult;
- cost += local;
- }
- }
-
- return cost;
- }
-
- 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 (!doing_slice && !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 the_new = c;
- bool swap = false;
- if (a != null) {
- if (doing_slice) {
- double cost = AssertionCost(a);
- bool first = (slice_limit - cost) >= 0 || slice_initial_limit == slice_limit;
- slice_limit -= cost;
- swap = slice_pos == first;
- } else if (assert_to_assume) {
- swap = true;
- } else {
- Contract.Assert(false);
- throw new cce.UnreachableException();
- }
-
- if (swap) {
- the_new = AssertTurnedIntoAssume(a);
- }
- }
- res.Add(the_new);
- }
- return res;
- }
-
- Block CloneBlock(Block b) {
- Contract.Requires(b != null);
- Contract.Ensures(Contract.Result<Block>() != null);
-
- Block res;
- if (copies.TryGetValue(b, out res)) {
- return cce.NonNull(res);
- }
- res = new Block(b.tok, b.Label, SliceCmds(b), b.TransferCmd);
- GotoCmd gt = b.TransferCmd as GotoCmd;
- copies[b] = res;
- if (gt != null) {
- GotoCmd newGoto = new GotoCmd(gt.tok, new List<String>(), new List<Block>());
- res.TransferCmd = newGoto;
- int pos = 0;
- foreach (Block ch in cce.NonNull(gt.labelTargets)) {
- Contract.Assert(ch != null);
- Contract.Assert(doing_slice ||
- (assert_to_assume || (keep_at_all.Contains(ch) || assumized_branches.Contains(ch))));
- if (doing_slice ||
- ((b != split_block || assumized_branches.Contains(ch) == assert_to_assume) &&
- keep_at_all.Contains(ch))) {
- newGoto.AddTarget(CloneBlock(ch));
- }
- pos++;
- }
- }
- return res;
- }
-
- Split DoSplit() {
- Contract.Ensures(Contract.Result<Split>() != null);
-
- copies.Clear();
- CloneBlock(blocks[0]);
- List<Block> newBlocks = new List<Block>();
- Dictionary<TransferCmd, ReturnCmd> newGotoCmdOrigins = new Dictionary<TransferCmd, ReturnCmd>();
- foreach (Block b in blocks) {
- Contract.Assert(b != null);
- Block tmp;
- if (copies.TryGetValue(b, out tmp)) {
- newBlocks.Add(cce.NonNull(tmp));
- if (gotoCmdOrigins.ContainsKey(b.TransferCmd)) {
- newGotoCmdOrigins[tmp.TransferCmd] = gotoCmdOrigins[b.TransferCmd];
- }
-
- foreach (Block p in b.Predecessors) {
- Contract.Assert(p != null);
- Block tmp2;
- if (copies.TryGetValue(p, out tmp2)) {
- tmp.Predecessors.Add(tmp2);
- }
- }
- }
- }
-
- return new Split(newBlocks, newGotoCmdOrigins, parent, impl);
- }
-
- Split SplitAt(int idx) {
- Contract.Ensures(Contract.Result<Split>() != null);
-
- assert_to_assume = idx == 0;
- doing_slice = false;
- ComputeBlockSets(true);
-
- return DoSplit();
- }
-
- Split SliceAsserts(double limit, bool pos) {
- Contract.Ensures(Contract.Result<Split>() != null);
-
- slice_pos = pos;
- slice_limit = limit;
- slice_initial_limit = limit;
- doing_slice = true;
- Split r = DoSplit();
- /*
- Console.WriteLine("split {0} / {1} -->", limit, pos);
- List<Block!> tmp = impl.Blocks;
- impl.Blocks = r.blocks;
- EmitImpl(impl, false);
- impl.Blocks = tmp;
- */
-
- return r;
- }
-
- void Print() {
- List<Block> tmp = impl.Blocks;
- Contract.Assert(tmp != null);
- impl.Blocks = blocks;
- EmitImpl(impl, false);
- impl.Blocks = tmp;
- }
-
- public Counterexample ToCounterexample(ProverContext context) {
- Contract.Requires(context != null);
- Contract.Ensures(Contract.Result<Counterexample>() != null);
-
- List<Block> trace = new List<Block>();
- foreach (Block b in blocks) {
- Contract.Assert(b != null);
- trace.Add(b);
- }
- foreach (Block b in blocks) {
- Contract.Assert(b != null);
- foreach (Cmd c in b.Cmds) {
- Contract.Assert(c != null);
- if (c is AssertCmd) {
- return AssertCmdToCounterexample((AssertCmd)c, cce.NonNull(b.TransferCmd), trace, null, null, context);
- }
- }
- }
- Contract.Assume(false);
- throw new cce.UnreachableException();
- }
-
- /// <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="begin_split_id">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 begin_split_id, 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 && begin_split_id == -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 split_here_count = 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 begin_split_id we were given and the next split
- if (split_here_count == begin_split_id) {
- verify = true;
- } else if (split_here_count == begin_split_id + 1) {
- verify = false;
- }
- } else { // We're in an endPoint so we stop verifying as soon as we hit a "split_here"
- verify = false;
- }
- split_here_count++;
- }
-
- var asrt = c as AssertCmd;
- if (verify || asrt == null)
- newCmds.Add(c);
- else
- newCmds.Add(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 List<Split/*!*/> FindManualSplits(Implementation/*!*/ impl, Dictionary<TransferCmd, ReturnCmd>/*!*/ gotoCmdOrigins, VCGen/*!*/ par) {
- Contract.Requires(impl != null);
- Contract.Ensures(Contract.Result<List<Split>>() == null || cce.NonNullElements(Contract.Result<List<Split>>()));
-
- var splitPoints = new Dictionary<Block,int>();
- foreach (var b in impl.Blocks) {
- foreach (Cmd c in b.Cmds) {
- var p = c as PredicateCmd;
- if (p != null && QKeyValue.FindBoolAttribute(p.Attributes, "split_here")) {
- int count;
- splitPoints.TryGetValue(b, out count);
- splitPoints[b] = count + 1;
- }
- }
- }
-
- if (splitPoints.Count() == 0) { // No manual split points here
- return null;
- }
-
- List<Split> splits = new List<Split>();
- Block entryPoint = impl.Blocks[0];
- var newEntryBlocks = DoManualSplit(impl.Blocks, entryPoint, -1, splitPoints.Keys.Contains(entryPoint), splitPoints.Keys);
- splits.Add(new Split(newEntryBlocks, gotoCmdOrigins, par, impl)); // REVIEW: Does gotoCmdOrigins need to be changed at all?
-
- foreach (KeyValuePair<Block,int> pair in splitPoints) {
- for (int i = 0; i < pair.Value; i++) {
- bool blockInternalSplit = i < pair.Value - 1; // There's at least one more split, after this one, in the current block
- var newBlocks = DoManualSplit(impl.Blocks, pair.Key, i, blockInternalSplit, splitPoints.Keys);
- Split s = new Split(newBlocks, gotoCmdOrigins, par, impl); // REVIEW: Does gotoCmdOrigins need to be changed at all?
- splits.Add(s);
- }
- }
-
- return splits;
- }
-
- public static List<Split/*!*/>/*!*/ DoSplit(Split initial, double max_cost, int max) {
- Contract.Requires(initial != null);
- Contract.Ensures(cce.NonNullElements(Contract.Result<List<Split>>()));
-
- List<Split> res = new List<Split>();
- res.Add(initial);
-
- while (res.Count < max) {
- Split best = null;
- int best_idx = 0, pos = 0;
- foreach (Split s in res) {
- Contract.Assert(s != null);
- s.ComputeBestSplit(); // TODO check total_cost first
- if (s.total_cost > max_cost &&
- (best == null || best.total_cost < s.total_cost) &&
- (s.assertion_count > 1 || s.split_block != null)) {
- best = s;
- best_idx = pos;
- }
- pos++;
- }
-
- if (best == null)
- break; // no split found
-
- Split s0, s1;
-
- bool split_stats = CommandLineOptions.Clo.TraceVerify;
-
- if (split_stats) {
- Console.WriteLine("{0} {1} -->", best.split_block == null ? "SLICE" : ("SPLIT@" + best.split_block.Label), best.Stats);
- if (best.split_block != null) {
- GotoCmd g = best.split_block.TransferCmd as GotoCmd;
- if (g != null) {
- Console.Write(" exits: ");
- foreach (Block b in cce.NonNull(g.labelTargets)) {
- Contract.Assert(b != null);
- Console.Write("{0} ", b.Label);
- }
- Console.WriteLine("");
- Console.Write(" assumized: ");
- foreach (Block b in best.assumized_branches) {
- Contract.Assert(b != null);
- Console.Write("{0} ", b.Label);
- }
- Console.WriteLine("");
- }
- }
- }
-
- if (best.split_block != null) {
- s0 = best.SplitAt(0);
- s1 = best.SplitAt(1);
- } else {
- best.split_block = null;
- s0 = best.SliceAsserts(best.assertion_cost / 2, true);
- s1 = best.SliceAsserts(best.assertion_cost / 2, false);
- }
-
- if (true) {
- List<Block> ss = new List<Block>();
- ss.Add(s0.blocks[0]);
- ss.Add(s1.blocks[0]);
- try {
- best.SoundnessCheck(new HashSet<List<Block>>(new BlockListComparer()), best.blocks[0], ss);
- } catch (System.Exception e) {
- Console.WriteLine(e);
- best.DumpDot(-1);
- s0.DumpDot(-2);
- s1.DumpDot(-3);
- Contract.Assert(false);
- throw new cce.UnreachableException();
- }
- }
-
- if (split_stats) {
- s0.ComputeBestSplit();
- s1.ComputeBestSplit();
- Console.WriteLine(" --> {0}", s0.Stats);
- Console.WriteLine(" --> {0}", s1.Stats);
- }
-
- if (CommandLineOptions.Clo.TraceVerify) {
- best.Print();
- }
-
- res[best_idx] = s0;
- res.Add(s1);
- }
-
- return res;
- }
-
- class BlockListComparer : IEqualityComparer<List<Block>>
- {
- public bool Equals(List<Block> x, List<Block> y)
- {
- return x == y || x.SequenceEqual(y);
- }
-
- public int GetHashCode(List<Block> obj)
- {
- int h = 0;
- Contract.Assume(obj != null);
- foreach (var b in obj)
- {
- if (b != null)
- {
- h += b.GetHashCode();
- }
- }
- return h;
- }
- }
-
- public Checker Checker {
- get {
- Contract.Ensures(Contract.Result<Checker>() != null);
-
- Contract.Assert(checker != null);
- return checker;
- }
- }
-
- public Task ProverTask {
- get {
- Contract.Assert(checker != null);
- return checker.ProverTask;
- }
- }
-
- public void ReadOutcome(ref Outcome cur_outcome, out bool prover_failed) {
- Contract.EnsuresOnThrow<UnexpectedProverOutputException>(true);
- ProverInterface.Outcome outcome = cce.NonNull(checker).ReadOutcome();
-
- if (CommandLineOptions.Clo.Trace && splitNo >= 0) {
- System.Console.WriteLine(" --> split #{0} done, [{1} s] {2}", splitNo, checker.ProverRunTime.TotalSeconds, outcome);
- }
-
- if (CommandLineOptions.Clo.VcsDumpSplits) {
- DumpDot(splitNo);
- }
-
- prover_failed = false;
-
- switch (outcome) {
- case ProverInterface.Outcome.Valid:
- return;
- case ProverInterface.Outcome.Invalid:
- cur_outcome = Outcome.Errors;
- return;
- case ProverInterface.Outcome.OutOfMemory:
- prover_failed = true;
- if (cur_outcome != Outcome.Errors && cur_outcome != Outcome.Inconclusive)
- cur_outcome = Outcome.OutOfMemory;
- return;
- case ProverInterface.Outcome.TimeOut:
- prover_failed = true;
- if (cur_outcome != Outcome.Errors && cur_outcome != Outcome.Inconclusive)
- cur_outcome = Outcome.TimedOut;
- return;
- case ProverInterface.Outcome.Undetermined:
- if (cur_outcome != Outcome.Errors)
- cur_outcome = Outcome.Inconclusive;
- return;
- default:
- Contract.Assert(false);
- throw new cce.UnreachableException();
- }
- }
-
- /// <summary>
- /// As a side effect, updates "this.parent.CumulativeAssertionCount".
- /// </summary>
- public void BeginCheck(Checker checker, VerifierCallback callback, ModelViewInfo mvInfo, int no, int timeout)
- {
- Contract.Requires(checker != null);
- Contract.Requires(callback != null);
-
- splitNo = no;
-
- impl.Blocks = blocks;
-
- this.checker = checker;
-
- Dictionary<int, Absy> label2absy = new Dictionary<int, Absy>();
-
- ProverContext ctx = checker.TheoremProver.Context;
- Boogie2VCExprTranslator bet = ctx.BoogieExprTranslator;
- CodeExprConversionClosure cc = new CodeExprConversionClosure(label2absy, ctx);
- bet.SetCodeExprConverter(cc.CodeExprToVerificationCondition);
-
- var exprGen = ctx.ExprGen;
- VCExpr controlFlowVariableExpr = CommandLineOptions.Clo.UseLabels ? null : exprGen.Integer(BigNum.ZERO);
-
- VCExpr vc = parent.GenerateVCAux(impl, controlFlowVariableExpr, label2absy, checker.TheoremProver.Context);
- Contract.Assert(vc != null);
-
- if (!CommandLineOptions.Clo.UseLabels)
- {
- VCExpr controlFlowFunctionAppl = exprGen.ControlFlowFunctionApplication(exprGen.Integer(BigNum.ZERO), exprGen.Integer(BigNum.ZERO));
- VCExpr eqExpr = exprGen.Eq(controlFlowFunctionAppl, exprGen.Integer(BigNum.FromInt(impl.Blocks[0].UniqueId)));
- vc = exprGen.Implies(eqExpr, vc);
- }
-
- if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Local)
- {
- reporter = new ErrorReporterLocal(gotoCmdOrigins, label2absy, impl.Blocks, parent.incarnationOriginMap, callback, mvInfo, cce.NonNull(this.Checker.TheoremProver.Context), parent.program);
- }
- else
- {
- reporter = new ErrorReporter(gotoCmdOrigins, label2absy, impl.Blocks, parent.incarnationOriginMap, callback, mvInfo, this.Checker.TheoremProver.Context, parent.program);
- }
-
- if (CommandLineOptions.Clo.TraceVerify && no >= 0)
- {
- Console.WriteLine("-- after split #{0}", no);
- Print();
- }
-
- string desc = cce.NonNull(impl.Name);
- if (no >= 0)
- desc += "_split" + no;
- checker.BeginCheck(desc, vc, reporter);
- }
-
- private void SoundnessCheck(HashSet<List<Block>/*!*/>/*!*/ cache, Block/*!*/ orig, List<Block/*!*/>/*!*/ copies) {
- Contract.Requires(cce.NonNull(cache));
- Contract.Requires(orig != null);
- Contract.Requires(copies != null);
- {
- var t = new List<Block> { orig };
- foreach (Block b in copies) {
- Contract.Assert(b != null);
- t.Add(b);
- }
- if (cache.Contains(t)) {
- return;
- }
- cache.Add(t);
- }
-
- for (int i = 0; i < orig.Cmds.Count; ++i) {
- Cmd cmd = orig.Cmds[i];
- if (cmd is AssertCmd) {
- int found = 0;
- foreach (Block c in copies) {
- Contract.Assert(c != null);
- if (c.Cmds[i] == cmd) {
- found++;
- }
- }
- if (found == 0) {
- throw new System.Exception(string.Format("missing assertion: {0}({1})", cmd.tok.filename, cmd.tok.line));
- }
- }
- }
-
- foreach (Block exit in Exits(orig)) {
- Contract.Assert(exit != null);
- List<Block> newcopies = new List<Block>();
- foreach (Block c in copies) {
- foreach (Block cexit in Exits(c)) {
- Contract.Assert(cexit != null);
- if (cexit.Label == exit.Label) {
- newcopies.Add(cexit);
- }
- }
- }
- if (newcopies.Count == 0) {
- throw new System.Exception("missing exit " + exit.Label);
- }
- SoundnessCheck(cache, exit, newcopies);
- }
- }
- }
- #endregion
-
-
- public class CodeExprConversionClosure
- {
- Dictionary<int, Absy> label2absy;
- ProverContext ctx;
- public CodeExprConversionClosure(Dictionary<int, Absy> label2absy, ProverContext ctx)
- {
- this.label2absy = label2absy;
- this.ctx = ctx;
- }
-
- public VCExpr CodeExprToVerificationCondition(CodeExpr codeExpr, Hashtable blockVariables, List<VCExprLetBinding> bindings, bool isPositiveContext)
- {
- VCGen vcgen = new VCGen(new Program(), null, false, new List<Checker>());
- vcgen.variable2SequenceNumber = new Dictionary<Variable, int>();
- vcgen.incarnationOriginMap = new Dictionary<Incarnation, Absy>();
- vcgen.CurrentLocalVariables = codeExpr.LocVars;
-
- ResetPredecessors(codeExpr.Blocks);
- vcgen.AddBlocksBetween(codeExpr.Blocks);
- Dictionary<Variable, Expr> gotoCmdOrigins = vcgen.ConvertBlocks2PassiveCmd(codeExpr.Blocks, new List<IdentifierExpr>(), new ModelViewInfo(codeExpr));
- int ac; // computed, but then ignored for this CodeExpr
- VCExpr startCorrect = VCGen.LetVCIterative(codeExpr.Blocks, null, label2absy, ctx, out ac, isPositiveContext);
- VCExpr vce = ctx.ExprGen.Let(bindings, startCorrect);
- if (vcgen.CurrentLocalVariables.Count != 0)
- {
- Boogie2VCExprTranslator translator = ctx.BoogieExprTranslator;
- List<VCExprVar> boundVars = new List<VCExprVar>();
- foreach (Variable v in vcgen.CurrentLocalVariables)
- {
- Contract.Assert(v != null);
- VCExprVar ev = translator.LookupVariable(v);
- Contract.Assert(ev != null);
- boundVars.Add(ev);
- if (v.TypedIdent.Type.Equals(Bpl.Type.Bool))
- {
- // add an antecedent (tickleBool ev) to help the prover find a possible trigger
- vce = ctx.ExprGen.Implies(ctx.ExprGen.Function(VCExpressionGenerator.TickleBoolOp, ev), vce);
- }
- }
- vce = ctx.ExprGen.Forall(boundVars, new List<VCTrigger>(), vce);
- }
- if (isPositiveContext)
- {
- vce = ctx.ExprGen.Not(vce);
- }
- return vce;
- }
- }
-
- public VCExpr GenerateVC(Implementation/*!*/ impl, VCExpr controlFlowVariableExpr, out Dictionary<int, Absy>/*!*/ label2absy, ProverContext proverContext)
- {
- Contract.Requires(impl != null);
- Contract.Requires(proverContext != null);
- Contract.Ensures(Contract.ValueAtReturn(out label2absy) != null);
- Contract.Ensures(Contract.Result<VCExpr>() != null);
-
- label2absy = new Dictionary<int, Absy>();
- return GenerateVCAux(impl, controlFlowVariableExpr, label2absy, proverContext);
- }
-
- public VCExpr GenerateVCAux(Implementation/*!*/ impl, VCExpr controlFlowVariableExpr, Dictionary<int, Absy>/*!*/ label2absy, ProverContext proverContext) {
- Contract.Requires(impl != null);
- Contract.Requires(proverContext != null);
- Contract.Ensures(Contract.Result<VCExpr>() != null);
-
- TypecheckingContext tc = new TypecheckingContext(null);
- impl.Typecheck(tc);
-
- VCExpr vc;
- int assertionCount;
- switch (CommandLineOptions.Clo.vcVariety) {
- case CommandLineOptions.VCVariety.Structured:
- vc = VCViaStructuredProgram(impl, label2absy, proverContext, out assertionCount);
- break;
- case CommandLineOptions.VCVariety.Block:
- vc = FlatBlockVC(impl, label2absy, false, false, false, proverContext, out assertionCount);
- break;
- case CommandLineOptions.VCVariety.BlockReach:
- vc = FlatBlockVC(impl, label2absy, false, true, false, proverContext, out assertionCount);
- break;
- case CommandLineOptions.VCVariety.Local:
- vc = FlatBlockVC(impl, label2absy, true, false, false, proverContext, out assertionCount);
- break;
- case CommandLineOptions.VCVariety.BlockNested:
- vc = NestedBlockVC(impl, label2absy, false, proverContext, out assertionCount);
- break;
- case CommandLineOptions.VCVariety.BlockNestedReach:
- vc = NestedBlockVC(impl, label2absy, true, proverContext, out assertionCount);
- break;
- case CommandLineOptions.VCVariety.Dag:
- if (cce.NonNull(CommandLineOptions.Clo.TheProverFactory).SupportsDags || CommandLineOptions.Clo.FixedPointEngine != null) {
- vc = DagVC(cce.NonNull(impl.Blocks[0]), controlFlowVariableExpr, label2absy, new Hashtable/*<Block, VCExpr!>*/(), proverContext, out assertionCount);
- } else {
- vc = LetVC(cce.NonNull(impl.Blocks[0]), controlFlowVariableExpr, label2absy, proverContext, out assertionCount);
- }
- break;
- case CommandLineOptions.VCVariety.DagIterative:
- vc = LetVCIterative(impl.Blocks, controlFlowVariableExpr, label2absy, proverContext, out assertionCount);
- break;
- case CommandLineOptions.VCVariety.Doomed:
- vc = FlatBlockVC(impl, label2absy, false, false, true, proverContext, out assertionCount);
- break;
- default:
- Contract.Assert(false);
- throw new cce.UnreachableException(); // unexpected enumeration value
- }
- CumulativeAssertionCount += assertionCount;
- return vc;
- }
-
- void CheckIntAttributeOnImpl(Implementation impl, string name, ref int val) {
- Contract.Requires(impl != null);
- Contract.Requires(name != null);
- if (!(cce.NonNull(impl.Proc).CheckIntAttribute(name, ref val) || !impl.CheckIntAttribute(name, ref val))) {
- Console.WriteLine("ignoring ill-formed {:{0} ...} attribute on {1}, parameter should be an int", name, impl.Name);
- }
- }
-
- public override Outcome VerifyImplementation(Implementation/*!*/ impl, VerifierCallback/*!*/ callback) {
- Contract.EnsuresOnThrow<UnexpectedProverOutputException>(true);
-
- if (impl.SkipVerification) {
- return Outcome.Inconclusive; // not sure about this one
- }
-
- callback.OnProgress("VCgen", 0, 0, 0.0);
-
- Stopwatch watch = new Stopwatch();
- if (_print_time)
- {
- Console.WriteLine("Checking function {0}", impl.Name);
- watch.Reset();
- watch.Start();
- }
-
- ConvertCFG2DAG(impl);
-
- SmokeTester smoke_tester = null;
- if (CommandLineOptions.Clo.SoundnessSmokeTest) {
- smoke_tester = new SmokeTester(this, impl, callback);
- smoke_tester.Copy();
- }
-
- ModelViewInfo mvInfo;
- var gotoCmdOrigins = PassifyImpl(impl, out mvInfo);
-
- // If "expand" attribute is supplied, expand any assertion of conjunctions into multiple assertions, one per conjunct
- foreach (var b in impl.Blocks)
- {
- List<Cmd> newCmds = new List<Cmd>();
- bool changed = false;
- foreach (var c in b.Cmds)
- {
- var a = c as AssertCmd;
- var ar = c as AssertRequiresCmd;
- var ae = c as AssertEnsuresCmd;
- var ai = c as LoopInitAssertCmd;
- var am = c as LoopInvMaintainedAssertCmd;
- // TODO:
- //use Duplicator and Substituter rather than new
- //nested IToken?
- //document expand attribute (search for {:ignore}, for example)
- //fix up new CallCmd, new Requires, new Ensures in OwickiGries.cs
- Func<Expr,Expr,Expr> withType = (Expr from, Expr to) =>
- {
- NAryExpr nFrom = from as NAryExpr;
- NAryExpr nTo = to as NAryExpr;
- to.Type = from.Type;
- if (nFrom != null && nTo != null) nTo.TypeParameters = nFrom.TypeParameters;
- return to;
- };
-
- Action<int,Expr,Action<Expr>> traverse = null;
- traverse = (depth, e, act) =>
- {
- ForallExpr forall = e as ForallExpr;
- NAryExpr nary = e as NAryExpr;
- if (forall != null)
- {
- traverse(depth, forall.Body, e1 => act(withType(forall,
- new ForallExpr(e1.tok, forall.TypeParameters, forall.Dummies, forall.Attributes, forall.Triggers, e1))));
- return;
- }
- if (nary != null)
- {
- var args = nary.Args;
- IAppliable fun = nary.Fun;
- BinaryOperator bop = fun as BinaryOperator;
- FunctionCall call = fun as FunctionCall;
- if (bop != null)
- {
- switch (bop.Op)
- {
- case BinaryOperator.Opcode.And:
- traverse(depth, args[0], act);
- traverse(depth, args[1], act);
- return;
- case BinaryOperator.Opcode.Imp:
- traverse(depth, args[1], e1 => act(withType(nary,
- new NAryExpr(e1.tok, fun, new List<Expr>() { args[0], e1 }))));
- return;
- }
- }
- if (depth > 0 && call != null && call.Func != null)
- {
- Function cf = call.Func;
- Expr body = cf.Body;
- List<Variable> ins = cf.InParams;
- if (body == null && cf.DefinitionAxiom != null)
- {
- ForallExpr all = cf.DefinitionAxiom.Expr as ForallExpr;
- if (all != null)
- {
- NAryExpr def = all.Body as NAryExpr;
- if (def != null && def.Fun is BinaryOperator && ((BinaryOperator) (def.Fun)).Op == BinaryOperator.Opcode.Iff)
- {
- body = def.Args[1];
- ins = all.Dummies;
- }
- }
- }
- if (body != null)
- {
- Func<Expr,Expr> new_f = e1 =>
- {
- Function f = new Function(cf.tok, "expand<" + cf.Name + ">", cf.TypeParameters, ins, cf.OutParams[0], cf.Comment);
- f.Body = e1;
- Token tok = new Token(e1.tok.line, e1.tok.col);
- tok.filename = e.tok.filename + "(" + e.tok.line + "," + e.tok.col + ") --> " + e1.tok.filename;
- return withType(nary, new NAryExpr(tok, new FunctionCall(f), args));
- };
- traverse(depth - 1, body, e1 => act(new_f(e1)));
- return;
- }
- }
- }
- act(e);
- };
-
- if (a != null)
- {
- var attr = a.Attributes;
- if (ar != null && ar.Requires.Attributes != null) attr = ar.Requires.Attributes;
- if (ar != null && ar.Call.Attributes != null) attr = ar.Call.Attributes;
- if (ae != null && ae.Ensures.Attributes != null) attr = ae.Ensures.Attributes;
- if (QKeyValue.FindExprAttribute(attr, "expand") != null || QKeyValue.FindBoolAttribute(attr, "expand"))
- {
- int depth = QKeyValue.FindIntAttribute(attr, "expand", 100);
- Func<Expr,Expr> fe = e => Expr.Or(a.Expr, e);
- //traverse(depth, a.Expr, e => System.Console.WriteLine(e.GetType() + " :: " + e + " @ " + e.tok.line + ", " + e.tok.col));
- traverse(depth, a.Expr, e =>
- {
- AssertCmd new_c =
- (ar != null) ? new AssertRequiresCmd(ar.Call, new Requires(e.tok, ar.Requires.Free, fe(e), ar.Requires.Comment)) :
- (ae != null) ? new AssertEnsuresCmd(new Ensures(e.tok, ae.Ensures.Free, fe(e), ae.Ensures.Comment)) :
- (ai != null) ? new LoopInitAssertCmd(e.tok, fe(e)) :
- (am != null) ? new LoopInvMaintainedAssertCmd(e.tok, fe(e)) :
- new AssertCmd(e.tok, fe(e));
- new_c.Attributes = new QKeyValue(e.tok, "subsumption", new List<object>() { new LiteralExpr(e.tok, BigNum.FromInt(0)) }, a.Attributes);
- newCmds.Add(new_c);
- });
- }
- newCmds.Add(c);
- changed = true;
- }
- else
- {
- newCmds.Add(c);
- }
- }
- if (changed) b.Cmds = newCmds;
- }
-
- double max_vc_cost = CommandLineOptions.Clo.VcsMaxCost;
- int tmp_max_vc_cost = -1, max_splits = CommandLineOptions.Clo.VcsMaxSplits,
- max_kg_splits = CommandLineOptions.Clo.VcsMaxKeepGoingSplits;
- CheckIntAttributeOnImpl(impl, "vcs_max_cost", ref tmp_max_vc_cost);
- CheckIntAttributeOnImpl(impl, "vcs_max_splits", ref max_splits);
- CheckIntAttributeOnImpl(impl, "vcs_max_keep_going_splits", ref max_kg_splits);
- if (tmp_max_vc_cost >= 0) {
- max_vc_cost = tmp_max_vc_cost;
- }
-
- Outcome outcome = Outcome.Correct;
-
- // Report all recycled failing assertions for this implementation.
- if (impl.RecycledFailingAssertions != null && impl.RecycledFailingAssertions.Any())
- {
- outcome = Outcome.Errors;
- foreach (var a in impl.RecycledFailingAssertions)
- {
- var checksum = a.Checksum;
- var oldCex = impl.ErrorChecksumToCachedError[checksum] as Counterexample;
- if (oldCex != null)
- {
- callback.OnCounterexample(oldCex, null);
- }
- }
- }
-
- Cores = CommandLineOptions.Clo.VcsCores;
- Stack<Split> work = new Stack<Split>();
- List<Split> currently_running = new List<Split>();
- ResetPredecessors(impl.Blocks);
- List<Split> manual_splits = Split.FindManualSplits(impl, gotoCmdOrigins, this);
- if (manual_splits != null) {
- foreach (var split in manual_splits) {
- work.Push(split);
- }
- } else {
- work.Push(new Split(impl.Blocks, gotoCmdOrigins, this, impl));
- }
-
- bool keep_going = max_kg_splits > 1;
- int total = 0;
- int no = max_splits == 1 && !keep_going ? -1 : 0;
- bool first_round = true;
- bool do_splitting = keep_going || max_splits > 1;
- double remaining_cost = 0.0, proven_cost = 0.0;
-
- if (do_splitting) {
- remaining_cost = work.Peek().Cost;
- }
-
- while (work.Any() || currently_running.Any())
- {
- bool prover_failed = false;
- Split s = null;
- var isWaiting = !work.Any();
-
- if (!isWaiting)
- {
- s = work.Peek();
-
- if (first_round && max_splits > 1)
- {
- prover_failed = true;
- remaining_cost -= s.Cost;
- }
- else
- {
- var timeout = (keep_going && s.LastChance) ? CommandLineOptions.Clo.VcsFinalAssertTimeout :
- keep_going ? CommandLineOptions.Clo.VcsKeepGoingTimeout :
- impl.TimeLimit;
-
- var checker = s.parent.FindCheckerFor(timeout, false);
- try
- {
- if (checker == null)
- {
- isWaiting = true;
- goto waiting;
- }
- else
- {
- s = work.Pop();
- }
-
- if (CommandLineOptions.Clo.Trace && no >= 0)
- {
- System.Console.WriteLine(" checking split {1}/{2}, {3:0.00}%, {0} ...",
- s.Stats, no + 1, total, 100 * proven_cost / (proven_cost + remaining_cost));
- }
- callback.OnProgress("VCprove", no < 0 ? 0 : no, total, proven_cost / (remaining_cost + proven_cost));
-
- Contract.Assert(s.parent == this);
- lock (checker)
- {
- s.BeginCheck(checker, callback, mvInfo, no, timeout);
- }
-
- no++;
-
- currently_running.Add(s);
- }
- catch (Exception)
- {
- checker.GoBackToIdle();
- throw;
- }
- }
- }
-
- waiting:
- if (isWaiting)
- {
- // Wait for one split to terminate.
- var tasks = currently_running.Select(splt => splt.ProverTask).ToArray();
-
- if (tasks.Any())
- {
- try
- {
- int index = Task.WaitAny(tasks);
- s = currently_running[index];
- currently_running.RemoveAt(index);
-
- if (do_splitting)
- {
- remaining_cost -= s.Cost;
- }
-
- lock (s.Checker)
- {
- s.ReadOutcome(ref outcome, out prover_failed);
- }
-
- if (do_splitting)
- {
- if (prover_failed)
- {
- // even if the prover fails, we have learned something, i.e., it is
- // annoying to watch Boogie say Timeout, 0.00% a couple of times
- proven_cost += s.Cost / 100;
- }
- else
- {
- proven_cost += s.Cost;
- }
- }
- callback.OnProgress("VCprove", no < 0 ? 0 : no, total, proven_cost / (remaining_cost + proven_cost));
-
- if (prover_failed && !first_round && s.LastChance)
- {
- string msg = "some timeout";
- if (s.reporter != null && s.reporter.resourceExceededMessage != null)
- {
- msg = s.reporter.resourceExceededMessage;
- }
- callback.OnCounterexample(s.ToCounterexample(s.Checker.TheoremProver.Context), msg);
- outcome = Outcome.Errors;
- break;
- }
- }
- finally
- {
- s.Checker.GoBackToIdle();
- }
-
- Contract.Assert(prover_failed || outcome == Outcome.Correct || outcome == Outcome.Errors || outcome == Outcome.Inconclusive);
- }
- }
-
- if (prover_failed)
- {
- int splits = first_round && max_splits > 1 ? max_splits : max_kg_splits;
-
- if (splits > 1)
- {
- List<Split> tmp = Split.DoSplit(s, max_vc_cost, splits);
- Contract.Assert(tmp != null);
- max_vc_cost = 1.0; // for future
- first_round = false;
- //tmp.Sort(new Comparison<Split!>(Split.Compare));
- foreach (Split a in tmp)
- {
- Contract.Assert(a != null);
- work.Push(a);
- total++;
- remaining_cost += a.Cost;
- }
- if (outcome != Outcome.Errors)
- {
- outcome = Outcome.Correct;
- }
- }
- else
- {
- Contract.Assert(outcome != Outcome.Correct);
- if (outcome == Outcome.TimedOut)
- {
- string msg = "some timeout";
- if (s.reporter != null && s.reporter.resourceExceededMessage != null)
- {
- msg = s.reporter.resourceExceededMessage;
- }
- callback.OnTimeout(msg);
- }
- else if (outcome == Outcome.OutOfMemory)
- {
- string msg = "out of memory";
- if (s.reporter != null && s.reporter.resourceExceededMessage != null)
- {
- msg = s.reporter.resourceExceededMessage;
- }
- callback.OnOutOfMemory(msg);
- }
-
- break;
- }
- }
- }
-
- if (outcome == Outcome.Correct && smoke_tester != null) {
- smoke_tester.Test();
- }
-
- callback.OnProgress("done", 0, 0, 1.0);
-
- if (_print_time)
- {
- watch.Stop();
- Console.WriteLine("Total time for this method: {0}", watch.Elapsed.ToString());
- }
-
- return outcome;
- }
-
- public class ErrorReporter : ProverInterface.ErrorHandler {
- Dictionary<TransferCmd, ReturnCmd>/*!*/ gotoCmdOrigins;
- Dictionary<int, Absy>/*!*/ label2absy;
- List<Block/*!*/>/*!*/ blocks;
- protected Dictionary<Incarnation, Absy/*!*/>/*!*/ incarnationOriginMap;
- protected VerifierCallback/*!*/ callback;
- protected ModelViewInfo MvInfo;
- internal string resourceExceededMessage;
- static System.IO.TextWriter modelWriter;
- [ContractInvariantMethod]
- void ObjectInvariant() {
- Contract.Invariant(gotoCmdOrigins != null);
- Contract.Invariant(label2absy != null);
- Contract.Invariant(cce.NonNullElements(blocks));
- Contract.Invariant(cce.NonNullDictionaryAndValues(incarnationOriginMap));
- Contract.Invariant(callback != null);
- Contract.Invariant(context != null);
- Contract.Invariant(program != null);
- }
-
-
- public static TextWriter ModelWriter {
- get {
- Contract.Ensures(Contract.Result<TextWriter>() != null);
-
- if (ErrorReporter.modelWriter == null)
- ErrorReporter.modelWriter = CommandLineOptions.Clo.PrintErrorModelFile == null ? Console.Out : new StreamWriter(CommandLineOptions.Clo.PrintErrorModelFile, false);
- return ErrorReporter.modelWriter;
- }
- }
-
- protected ProverContext/*!*/ context;
- Program/*!*/ program;
-
- public ErrorReporter(Dictionary<TransferCmd, ReturnCmd>/*!*/ gotoCmdOrigins,
- Dictionary<int, Absy>/*!*/ label2absy,
- List<Block/*!*/>/*!*/ blocks,
- Dictionary<Incarnation, Absy/*!*/>/*!*/ incarnationOriginMap,
- VerifierCallback/*!*/ callback,
- ModelViewInfo mvInfo,
- ProverContext/*!*/ context,
- Program/*!*/ program) {
- Contract.Requires(gotoCmdOrigins != null);
- Contract.Requires(label2absy != null);
- Contract.Requires(cce.NonNullElements(blocks));
- Contract.Requires(cce.NonNullDictionaryAndValues(incarnationOriginMap));
- Contract.Requires(callback != null);
- Contract.Requires(context!=null);
- Contract.Requires(program!=null);
- this.gotoCmdOrigins = gotoCmdOrigins;
- this.label2absy = label2absy;
- this.blocks = blocks;
- this.incarnationOriginMap = incarnationOriginMap;
- this.callback = callback;
- this.MvInfo = mvInfo;
-
- this.context = context;
- this.program = program;
- }
-
- public override void OnModel(IList<string/*!*/>/*!*/ labels, Model model, ProverInterface.Outcome proverOutcome) {
- //Contract.Requires(cce.NonNullElements(labels));
- if (CommandLineOptions.Clo.PrintErrorModel >= 1 && model != null) {
- if (VC.ConditionGeneration.errorModelList != null)
- {
- VC.ConditionGeneration.errorModelList.Add(model);
- }
-
- model.Write(ErrorReporter.ModelWriter);
- ErrorReporter.ModelWriter.Flush();
- }
-
- Hashtable traceNodes = new Hashtable();
- foreach (string s in labels) {
- Contract.Assert(s != null);
- Absy absy = Label2Absy(s);
- Contract.Assert(absy != null);
- if (traceNodes.ContainsKey(absy))
- System.Console.WriteLine("Warning: duplicate label: " + s + " read while tracing nodes");
- else
- traceNodes.Add(absy, null);
- }
-
- List<Block> trace = new List<Block>();
- Block entryBlock = cce.NonNull(this.blocks[0]);
- Contract.Assert(traceNodes.Contains(entryBlock));
- trace.Add(entryBlock);
-
- Counterexample newCounterexample = TraceCounterexample(entryBlock, traceNodes, trace, model, MvInfo, incarnationOriginMap, context, new Dictionary<TraceLocation, CalleeCounterexampleInfo>());
-
- if (newCounterexample == null)
- return;
-
- #region Map passive program errors back to original program errors
- ReturnCounterexample returnExample = newCounterexample as ReturnCounterexample;
- if (returnExample != null) {
- foreach (Block b in returnExample.Trace) {
- Contract.Assert(b != null);
- Contract.Assume(b.TransferCmd != null);
- ReturnCmd cmd = gotoCmdOrigins.ContainsKey(b.TransferCmd) ? gotoCmdOrigins[b.TransferCmd] : null;
- if (cmd != null) {
- returnExample.FailingReturn = cmd;
- break;
- }
- }
- }
- #endregion
- callback.OnCounterexample(newCounterexample, null);
- }
-
- public override Absy Label2Absy(string label) {
- //Contract.Requires(label != null);
- Contract.Ensures(Contract.Result<Absy>() != null);
-
- int id = int.Parse(label);
- return cce.NonNull((Absy)label2absy[id]);
- }
-
- public override void OnResourceExceeded(string msg) {
- //Contract.Requires(msg != null);
- resourceExceededMessage = msg;
- }
-
- public override void OnProverWarning(string msg) {
- //Contract.Requires(msg != null);
- callback.OnWarning(msg);
- }
- }
-
- public class ErrorReporterLocal : ErrorReporter {
- public ErrorReporterLocal(Dictionary<TransferCmd, ReturnCmd>/*!*/ gotoCmdOrigins,
- Dictionary<int, Absy>/*!*/ label2absy,
- List<Block/*!*/>/*!*/ blocks,
- Dictionary<Incarnation, Absy/*!*/>/*!*/ incarnationOriginMap,
- VerifierCallback/*!*/ callback,
- ModelViewInfo mvInfo,
- ProverContext/*!*/ context,
- Program/*!*/ program)
- : base(gotoCmdOrigins, label2absy, blocks, incarnationOriginMap, callback, mvInfo, context, program) // here for aesthetic purposes //TODO: Maybe nix?
- {
- Contract.Requires(gotoCmdOrigins != null);
- Contract.Requires(label2absy != null);
- Contract.Requires(cce.NonNullElements(blocks));
- Contract.Requires(cce.NonNullDictionaryAndValues(incarnationOriginMap));
- Contract.Requires(callback != null);
- Contract.Requires(context != null);
- Contract.Requires(program != null);
- }
-
- public override void OnModel(IList<string/*!*/>/*!*/ labels, Model model, ProverInterface.Outcome proverOutcome) {
- //Contract.Requires(cce.NonNullElements(labels));
- // We ignore the error model here for enhanced error message purposes.
- // It is only printed to the command line.
- if (CommandLineOptions.Clo.PrintErrorModel >= 1 && model != null) {
- if (CommandLineOptions.Clo.PrintErrorModelFile != null) {
- model.Write(ErrorReporter.ModelWriter);
- ErrorReporter.ModelWriter.Flush();
- }
- }
- List<Block> traceNodes = new List<Block>();
- List<AssertCmd> assertNodes = new List<AssertCmd>();
- foreach (string s in labels) {
- Contract.Assert(s != null);
- Absy node = Label2Absy(s);
- if (node is Block) {
- Block b = (Block)node;
- traceNodes.Add(b);
- } else {
- AssertCmd a = (AssertCmd)node;
- assertNodes.Add(a);
- }
- }
- Contract.Assert(assertNodes.Count > 0);
- Contract.Assert(traceNodes.Count == assertNodes.Count);
-
- foreach (AssertCmd a in assertNodes) {
- // find the corresponding Block (assertNodes.Count is likely to be 1, or small in any case, so just do a linear search here)
- foreach (Block b in traceNodes) {
- if (b.Cmds.Contains(a)) {
- List<Block> trace = new List<Block>();
- trace.Add(b);
- Counterexample newCounterexample = AssertCmdToCounterexample(a, cce.NonNull(b.TransferCmd), trace, model, MvInfo, context);
- callback.OnCounterexample(newCounterexample, null);
- goto NEXT_ASSERT;
- }
- }
- Contract.Assert(false);
- throw new cce.UnreachableException(); // there was no block that contains the assert
- NEXT_ASSERT: {
- }
- }
- }
- }
-
- private void RecordCutEdge(Dictionary<Block,List<Block>> edgesCut, Block from, Block to){
- if (edgesCut != null)
- {
- if (!edgesCut.ContainsKey(from))
- edgesCut.Add(from, new List<Block>());
- edgesCut[from].Add(to);
- }
- }
-
- public void ConvertCFG2DAG(Implementation impl, Dictionary<Block,List<Block>> edgesCut = null, int taskID = -1)
- {
- Contract.Requires(impl != null);
- impl.PruneUnreachableBlocks(); // This is needed for VCVariety.BlockNested, and is otherwise just an optimization
-
- CurrentLocalVariables = impl.LocVars;
- variable2SequenceNumber = new Dictionary<Variable, int>();
- incarnationOriginMap = new Dictionary<Incarnation, Absy>();
-
- #region Debug Tracing
- if (CommandLineOptions.Clo.TraceVerify)
- {
- Console.WriteLine("original implementation");
- EmitImpl(impl, false);
- }
- #endregion
-
- #region Debug Tracing
- if (CommandLineOptions.Clo.TraceVerify)
- {
- Console.WriteLine("after desugaring sugared commands like procedure calls");
- EmitImpl(impl, true);
- }
- #endregion
-
- // Recompute the predecessors, but first insert a dummy start node that is sure not to be the target of any goto (because the cutting of back edges
- // below assumes that the start node has no predecessor)
- impl.Blocks.Insert(0, new Block(new Token(-17, -4), "0", new List<Cmd>(), new GotoCmd(Token.NoToken, new List<String> { impl.Blocks[0].Label }, new List<Block> { impl.Blocks[0] })));
- ResetPredecessors(impl.Blocks);
-
- if(CommandLineOptions.Clo.KInductionDepth < 0) {
- ConvertCFG2DAGStandard(impl, edgesCut, taskID);
- } else {
- ConvertCFG2DAGKInduction(impl, edgesCut, taskID);
- }
-
- #region Debug Tracing
- if (CommandLineOptions.Clo.TraceVerify)
- {
- Console.WriteLine("after conversion into a DAG");
- EmitImpl(impl, true);
- }
- #endregion
- }
-
- private void ConvertCFG2DAGStandard(Implementation impl, Dictionary<Block, List<Block>> edgesCut, int taskID)
- {
- #region Convert program CFG into a DAG
-
- #region Use the graph library to figure out where the (natural) loops are
-
- #region Create the graph by adding the source node and each edge
- Graph<Block> g = Program.GraphFromImpl(impl);
- #endregion
-
- //Graph<Block> g = program.ProcessLoops(impl);
-
- g.ComputeLoops(); // this is the call that does all of the processing
- if (!g.Reducible)
- {
- throw new VCGenException("Irreducible flow graphs are unsupported.");
- }
-
- #endregion
-
- #region Cut the backedges, push assert/assume statements from loop header into predecessors, change them all into assume statements at top of loop, introduce havoc statements
- foreach (Block header in cce.NonNull(g.Headers))
- {
- Contract.Assert(header != null);
- IDictionary<Block, object> backEdgeNodes = new Dictionary<Block, object>();
- foreach (Block b in cce.NonNull(g.BackEdgeNodes(header)))
- {
- Contract.Assert(b != null);
- backEdgeNodes.Add(b, null);
- }
-
- #region Find the (possibly empty) prefix of assert commands in the header, replace each assert with an assume of the same condition
- List<Cmd> prefixOfPredicateCmdsInit = new List<Cmd>();
- List<Cmd> prefixOfPredicateCmdsMaintained = new List<Cmd>();
- for (int i = 0, n = header.Cmds.Count; i < n; i++)
- {
- PredicateCmd a = header.Cmds[i] as PredicateCmd;
- if (a != null)
- {
- if (a is AssertCmd)
- {
- AssertCmd c = (AssertCmd)a;
- AssertCmd b = null;
-
- if (CommandLineOptions.Clo.ConcurrentHoudini)
- {
- Contract.Assert(taskID >= 0);
- if (CommandLineOptions.Clo.Cho[taskID].DisableLoopInvEntryAssert)
- b = new LoopInitAssertCmd(c.tok, Expr.True);
- else
- b = new LoopInitAssertCmd(c.tok, c.Expr);
- }
- else
- {
- b = new LoopInitAssertCmd(c.tok, c.Expr);
- }
-
- b.Attributes = c.Attributes;
- b.ErrorData = c.ErrorData;
- prefixOfPredicateCmdsInit.Add(b);
-
- if (CommandLineOptions.Clo.ConcurrentHoudini)
- {
- Contract.Assert(taskID >= 0);
- if (CommandLineOptions.Clo.Cho[taskID].DisableLoopInvMaintainedAssert)
- b = new Bpl.LoopInvMaintainedAssertCmd(c.tok, Expr.True);
- else
- b = new Bpl.LoopInvMaintainedAssertCmd(c.tok, c.Expr);
- }
- else
- {
- b = new Bpl.LoopInvMaintainedAssertCmd(c.tok, c.Expr);
- }
-
- b.Attributes = c.Attributes;
- b.ErrorData = c.ErrorData;
- prefixOfPredicateCmdsMaintained.Add(b);
- header.Cmds[i] = new AssumeCmd(c.tok, c.Expr);
- }
- else
- {
- Contract.Assert(a is AssumeCmd);
- if (Bpl.CommandLineOptions.Clo.AlwaysAssumeFreeLoopInvariants)
- {
- // Usually, "free" stuff, like free loop invariants (and the assume statements
- // that stand for such loop invariants) are ignored on the checking side. This
- // command-line option changes that behavior to always assume the conditions.
- prefixOfPredicateCmdsInit.Add(a);
- prefixOfPredicateCmdsMaintained.Add(a);
- }
- }
- }
- else if (header.Cmds[i] is CommentCmd)
- {
- // ignore
- }
- else
- {
- break; // stop when an assignment statement (or any other non-predicate cmd) is encountered
- }
- }
- #endregion
-
- #region Copy the prefix of predicate commands into each predecessor. Do this *before* cutting the backedge!!
- for (int predIndex = 0, n = header.Predecessors.Count; predIndex < n; predIndex++)
- {
- Block pred = cce.NonNull(header.Predecessors[predIndex]);
-
- // Create a block between header and pred for the predicate commands if pred has more than one successor
- GotoCmd gotocmd = cce.NonNull((GotoCmd)pred.TransferCmd);
- Contract.Assert(gotocmd.labelNames != null); // if "pred" is really a predecessor, it may be a GotoCmd with at least one label
- if (gotocmd.labelNames.Count > 1)
- {
- Block newBlock = CreateBlockBetween(predIndex, header);
- impl.Blocks.Add(newBlock);
-
- // if pred is a back edge node, then now newBlock is the back edge node
- if (backEdgeNodes.ContainsKey(pred))
- {
- backEdgeNodes.Remove(pred);
- backEdgeNodes.Add(newBlock, null);
- }
-
- pred = newBlock;
- }
- // Add the predicate commands
- if (backEdgeNodes.ContainsKey(pred))
- {
- pred.Cmds.AddRange(prefixOfPredicateCmdsMaintained);
- }
- else
- {
- pred.Cmds.AddRange(prefixOfPredicateCmdsInit);
- }
- }
- #endregion
-
- #region Cut the back edge
- foreach (Block backEdgeNode in cce.NonNull(backEdgeNodes.Keys))
- {
- Contract.Assert(backEdgeNode != null);
- Debug.Assert(backEdgeNode.TransferCmd is GotoCmd, "An node was identified as the source for a backedge, but it does not have a goto command.");
- GotoCmd gtc = backEdgeNode.TransferCmd as GotoCmd;
- if (gtc != null && gtc.labelTargets != null && gtc.labelTargets.Count > 1)
- {
- // then remove the backedge by removing the target block from the list of gotos
- List<Block> remainingTargets = new List<Block>();
- List<String> remainingLabels = new List<String>();
- Contract.Assume(gtc.labelNames != null);
- for (int i = 0, n = gtc.labelTargets.Count; i < n; i++)
- {
- if (gtc.labelTargets[i] != header)
- {
- remainingTargets.Add(gtc.labelTargets[i]);
- remainingLabels.Add(gtc.labelNames[i]);
- }
- else
- RecordCutEdge(edgesCut, backEdgeNode, header);
- }
- gtc.labelTargets = remainingTargets;
- gtc.labelNames = remainingLabels;
- }
- else
- {
- // This backedge is the only out-going edge from this node.
- // Add an "assume false" statement to the end of the statements
- // inside of the block and change the goto command to a return command.
- AssumeCmd ac = new AssumeCmd(Token.NoToken, Expr.False);
- backEdgeNode.Cmds.Add(ac);
- backEdgeNode.TransferCmd = new ReturnCmd(Token.NoToken);
- if (gtc != null && gtc.labelTargets != null && gtc.labelTargets.Count == 1)
- RecordCutEdge(edgesCut, backEdgeNode, gtc.labelTargets[0]);
- }
- #region Remove the backedge node from the list of predecessor nodes in the header
- List<Block> newPreds = new List<Block>();
- foreach (Block p in header.Predecessors)
- {
- if (p != backEdgeNode)
- newPreds.Add(p);
- }
- header.Predecessors = newPreds;
- #endregion
- }
- #endregion
-
- #region Collect all variables that are assigned to in all of the natural loops for which this is the header
- List<Variable> varsToHavoc = VarsAssignedInLoop(g, header);
- List<IdentifierExpr> havocExprs = new List<IdentifierExpr>();
- foreach (Variable v in varsToHavoc)
- {
- Contract.Assert(v != null);
- IdentifierExpr ie = new IdentifierExpr(Token.NoToken, v);
- if (!havocExprs.Contains(ie))
- havocExprs.Add(ie);
- }
- // pass the token of the enclosing loop header to the HavocCmd so we can reconstruct
- // the source location for this later on
- HavocCmd hc = new HavocCmd(header.tok, havocExprs);
- List<Cmd> newCmds = new List<Cmd>();
- newCmds.Add(hc);
- foreach (Cmd c in header.Cmds)
- {
- newCmds.Add(c);
- }
- header.Cmds = newCmds;
- #endregion
- }
- #endregion
- #endregion Convert program CFG into a DAG
- }
-
- public static List<Variable> VarsAssignedInLoop(Graph<Block> g, Block header)
- {
- List<Variable> varsToHavoc = new List<Variable>();
- foreach (Block backEdgeNode in cce.NonNull(g.BackEdgeNodes(header)))
- {
- Contract.Assert(backEdgeNode != null);
- foreach (Block b in g.NaturalLoops(header, backEdgeNode))
- {
- Contract.Assert(b != null);
- foreach (Cmd c in b.Cmds)
- {
- Contract.Assert(c != null);
- c.AddAssignedVariables(varsToHavoc);
- }
- }
- }
- return varsToHavoc;
- }
-
- public static IEnumerable<Variable> VarsReferencedInLoop(Graph<Block> g, Block header)
- {
- HashSet<Variable> referencedVars = new HashSet<Variable>();
- foreach (Block backEdgeNode in cce.NonNull(g.BackEdgeNodes(header)))
- {
- Contract.Assert(backEdgeNode != null);
- foreach (Block b in g.NaturalLoops(header, backEdgeNode))
- {
- Contract.Assert(b != null);
- foreach (Cmd c in b.Cmds)
- {
- Contract.Assert(c != null);
- var Collector = new VariableCollector();
- Collector.Visit(c);
- foreach(var v in Collector.usedVars) {
- referencedVars.Add(v);
- }
- }
- }
- }
- return referencedVars;
- }
-
- private void ConvertCFG2DAGKInduction(Implementation impl, Dictionary<Block, List<Block>> edgesCut, int taskID) {
-
- // K-induction has not been adapted to be aware of these parameters which standard CFG to DAG transformation uses
- Contract.Requires(edgesCut == null);
- Contract.Requires(taskID == -1);
-
- int inductionK = CommandLineOptions.Clo.KInductionDepth;
- Contract.Assume(inductionK >= 0);
-
- bool contRuleApplication = true;
- while (contRuleApplication) {
- contRuleApplication = false;
-
- #region Use the graph library to figure out where the (natural) loops are
-
- #region Create the graph by adding the source node and each edge
- Graph<Block> g = Program.GraphFromImpl(impl);
- #endregion
-
- g.ComputeLoops(); // this is the call that does all of the processing
- if (!g.Reducible) {
- throw new VCGenException("Irreducible flow graphs are unsupported.");
- }
-
- #endregion
-
- foreach (Block header in cce.NonNull(g.Headers)) {
- Contract.Assert(header != null);
-
- #region Debug Tracing
- if (CommandLineOptions.Clo.TraceVerify)
- {
- Console.WriteLine("Applying k-induction rule with k=" + inductionK);
- }
- #endregion
-
- #region generate the step case
- Block newHeader = DuplicateLoop(impl, g, header, null,
- false, false, "_step_assertion");
- for (int i = 0; i < inductionK; ++i)
- {
- newHeader = DuplicateLoop(impl, g, header, newHeader,
- true, true,
- "_step_" + (inductionK - i));
- }
- #endregion
-
- #region havoc variables that can be assigned in the loop
-
- List<Variable> varsToHavoc = VarsAssignedInLoop(g, header);
- List<IdentifierExpr> havocExprs = new List<IdentifierExpr>();
- foreach (Variable v in varsToHavoc)
- {
- Contract.Assert(v != null);
- IdentifierExpr ie = new IdentifierExpr(Token.NoToken, v);
- if (!havocExprs.Contains(ie))
- havocExprs.Add(ie);
- }
- // pass the token of the enclosing loop header to the HavocCmd so we can reconstruct
- // the source location for this later on
- HavocCmd hc = new HavocCmd(newHeader.tok, havocExprs);
- List<Cmd> havocCmds = new List<Cmd>();
- havocCmds.Add(hc);
-
- Block havocBlock = new Block(newHeader.tok, newHeader.Label + "_havoc", havocCmds,
- new GotoCmd (newHeader.tok, new List<Block> { newHeader }));
-
- impl.Blocks.Add(havocBlock);
- newHeader.Predecessors.Add(havocBlock);
- newHeader = havocBlock;
-
- #endregion
-
- #region generate the base case loop copies
- for (int i = 0; i < inductionK; ++i)
- {
- newHeader = DuplicateLoop(impl, g, header, newHeader,
- false, false,
- "_base_" + (inductionK - i));
- }
- #endregion
-
- #region redirect into the new loop copies and remove the original loop (but don't redirect back-edges)
-
- IDictionary<Block, object> backEdgeNodes = new Dictionary<Block, object>();
- foreach (Block b in cce.NonNull(g.BackEdgeNodes(header))) { Contract.Assert(b != null); backEdgeNodes.Add(b, null); }
-
- for (int predIndex = 0, n = header.Predecessors.Count(); predIndex < n; predIndex++)
- {
- Block pred = cce.NonNull(header.Predecessors[predIndex]);
- if (!backEdgeNodes.ContainsKey(pred))
- {
- GotoCmd gc = pred.TransferCmd as GotoCmd;
- Contract.Assert(gc != null);
- for (int i = 0; i < gc.labelTargets.Count(); ++i)
- {
- if (gc.labelTargets[i] == header)
- {
- gc.labelTargets[i] = newHeader;
- gc.labelNames[i] = newHeader.Label;
- newHeader.Predecessors.Add(pred);
- }
- }
- }
- }
- impl.PruneUnreachableBlocks();
-
- #endregion
-
- contRuleApplication = true;
- break;
- }
-
- }
-
- ResetPredecessors(impl.Blocks);
- impl.FreshenCaptureStates();
-
- }
-
- private Block DuplicateLoop(Implementation impl, Graph<Block> g,
- Block header, Block nextHeader, bool cutExits,
- bool toAssumptions, string suffix)
- {
- IDictionary<Block, Block> ori2CopiedBlocks = new Dictionary<Block, Block>();
- Duplicator duplicator = new Duplicator();
-
- #region create copies of all blocks in the loop
- foreach (Block backEdgeNode in cce.NonNull(g.BackEdgeNodes(header)))
- {
- Contract.Assert(backEdgeNode != null);
- foreach (Block b in g.NaturalLoops(header, backEdgeNode))
- {
- Contract.Assert(b != null);
- if (!ori2CopiedBlocks.ContainsKey(b))
- {
- Block copy = (Block)duplicator.Visit(b);
- copy.Cmds = new List<Cmd>(copy.Cmds); // Philipp Ruemmer commented that this was necessary due to a bug in the Duplicator. That was a long time; worth checking whether this has been fixed
- copy.Predecessors = new List<Block>();
- copy.Label = copy.Label + suffix;
-
- #region turn asserts into assumptions
- if (toAssumptions)
- {
- for (int i = 0; i < copy.Cmds.Count(); ++i)
- {
- AssertCmd ac = copy.Cmds[i] as AssertCmd;
- if (ac != null)
- {
- copy.Cmds[i] = new AssumeCmd(ac.tok, ac.Expr);
- }
- }
- }
- #endregion
-
- impl.Blocks.Add(copy);
- ori2CopiedBlocks.Add(b, copy);
- }
- }
- }
- #endregion
-
- #region adjust the transfer commands of the newly created blocks
- foreach (KeyValuePair<Block, Block> pair in ori2CopiedBlocks)
- {
- Block copy = pair.Value;
- GotoCmd gc = copy.TransferCmd as GotoCmd;
- if (gc != null)
- {
- List<Block> newTargets = new List<Block>();
- List<string> newLabels = new List<string>();
-
- for (int i = 0; i < gc.labelTargets.Count(); ++i)
- {
- Block newTarget;
- if (gc.labelTargets[i] == header)
- {
- if (nextHeader != null)
- {
- newTargets.Add(nextHeader);
- newLabels.Add(nextHeader.Label);
- nextHeader.Predecessors.Add(copy);
- }
- }
- else if (ori2CopiedBlocks.TryGetValue(gc.labelTargets[i], out newTarget))
- {
- newTargets.Add(newTarget);
- newLabels.Add(newTarget.Label);
- newTarget.Predecessors.Add(copy);
- }
- else if (!cutExits)
- {
- newTargets.Add(gc.labelTargets[i]);
- newLabels.Add(gc.labelNames[i]);
- gc.labelTargets[i].Predecessors.Add(copy);
- }
- }
-
- if (newTargets.Count() == 0)
- {
- // if no targets are left, we assume false and return
- copy.Cmds.Add(new AssumeCmd(Token.NoToken, Expr.False));
- copy.TransferCmd = new ReturnCmd(Token.NoToken);
- }
- else
- {
- copy.TransferCmd = new GotoCmd(gc.tok, newLabels, newTargets);
- }
- }
- else if (cutExits && (copy.TransferCmd is ReturnCmd))
- {
- // because return is a kind of exit from the loop, we
- // assume false to cut this path
- copy.Cmds.Add(new AssumeCmd(Token.NoToken, Expr.False));
- }
- }
- #endregion
-
- return ori2CopiedBlocks[header];
- }
-
- public void DesugarCalls(Implementation impl) {
- foreach (Block block in impl.Blocks) {
- List<Cmd> newCmds = new List<Cmd>();
- foreach (Cmd cmd in block.Cmds) {
- SugaredCmd sugaredCmd = cmd as SugaredCmd;
- if (sugaredCmd != null) {
- StateCmd stateCmd = sugaredCmd.Desugaring as StateCmd;
- foreach (Variable v in stateCmd.Locals) {
- impl.LocVars.Add(v);
- }
- newCmds.AddRange(stateCmd.Cmds);
- }
- else {
- newCmds.Add(cmd);
- }
- }
- block.Cmds = newCmds;
- }
- }
-
- public Dictionary<TransferCmd, ReturnCmd> PassifyImpl(Implementation impl, out ModelViewInfo mvInfo)
- {
- Contract.Requires(impl != null);
- Contract.Requires(program != null);
- Contract.Ensures(Contract.Result<Dictionary<TransferCmd, ReturnCmd>>() != null);
-
- Dictionary<TransferCmd, ReturnCmd> gotoCmdOrigins = new Dictionary<TransferCmd, ReturnCmd>();
- Block exitBlock = GenerateUnifiedExit(impl, gotoCmdOrigins);
-
- #region Debug Tracing
- if (CommandLineOptions.Clo.TraceVerify)
- {
- Console.WriteLine("after creating a unified exit block");
- EmitImpl(impl, true);
- }
- #endregion
-
- #region Insert pre- and post-conditions and where clauses as assume and assert statements
- {
- List<Cmd> cc = new List<Cmd>();
- // where clauses of global variables
- lock (program.TopLevelDeclarations)
- {
- foreach (var gvar in program.GlobalVariables)
- {
- if (gvar != null && gvar.TypedIdent.WhereExpr != null)
- {
- Cmd c = new AssumeCmd(gvar.tok, gvar.TypedIdent.WhereExpr);
- cc.Add(c);
- }
- }
- }
- // where clauses of in- and out-parameters
- cc.AddRange(GetParamWhereClauses(impl));
- // where clauses of local variables
- foreach (Variable lvar in impl.LocVars) {Contract.Assert(lvar != null);
- if (lvar.TypedIdent.WhereExpr != null) {
- Cmd c = new AssumeCmd(lvar.tok, lvar.TypedIdent.WhereExpr);
- cc.Add(c);
- } else if (QKeyValue.FindBoolAttribute(lvar.Attributes, "assumption")) {
- cc.Add(new AssumeCmd(lvar.tok, new IdentifierExpr(lvar.tok, lvar), new QKeyValue(lvar.tok, "assumption_variable_initialization", new List<object>(), null)));
- }
- }
- // add cc and the preconditions to new blocks preceding impl.Blocks[0]
- InjectPreconditions(impl, cc);
-
- // append postconditions, starting in exitBlock and continuing into other blocks, if needed
- InjectPostConditions(impl, exitBlock, gotoCmdOrigins);
- }
- #endregion
-
- #region Support for stratified inlining
- addExitAssert(impl.Name, exitBlock);
- #endregion
-
-
- #region Debug Tracing
- if (CommandLineOptions.Clo.TraceVerify)
- {
- Console.WriteLine("after inserting pre- and post-conditions");
- EmitImpl(impl, true);
- }
- #endregion
-
- AddBlocksBetween(impl.Blocks);
-
- #region Debug Tracing
- if (CommandLineOptions.Clo.TraceVerify)
- {
- Console.WriteLine("after adding empty blocks as needed to catch join assumptions");
- EmitImpl(impl, true);
- }
- #endregion
-
- if (CommandLineOptions.Clo.LiveVariableAnalysis > 0) {
- Microsoft.Boogie.LiveVariableAnalysis.ComputeLiveVariables(impl);
- }
-
- mvInfo = new ModelViewInfo(program, impl);
- Convert2PassiveCmd(impl, mvInfo);
-
- #region Peep-hole optimizations
- if (CommandLineOptions.Clo.RemoveEmptyBlocks){
- #region Get rid of empty blocks
- {
- RemoveEmptyBlocksIterative(impl.Blocks);
- impl.PruneUnreachableBlocks();
- }
- #endregion Get rid of empty blocks
-
- #region Debug Tracing
- if (CommandLineOptions.Clo.TraceVerify)
- {
- Console.WriteLine("after peep-hole optimizations");
- EmitImpl(impl, true);
- }
- #endregion
- }
- #endregion Peep-hole optimizations
-
- HandleSelectiveChecking(impl);
-
-
-// #region Constant Folding
-// #endregion
-// #region Debug Tracing
-// if (CommandLineOptions.Clo.TraceVerify)
-// {
-// Console.WriteLine("after constant folding");
-// EmitImpl(impl, true);
-// }
-// #endregion
-
- return gotoCmdOrigins;
- }
-
- private static void HandleSelectiveChecking(Implementation impl)
- {
- if (QKeyValue.FindBoolAttribute(impl.Attributes, "selective_checking") ||
- QKeyValue.FindBoolAttribute(impl.Proc.Attributes, "selective_checking")) {
-
- var startPoints = new List<Block>();
- foreach (var b in impl.Blocks) {
- foreach (Cmd c in b.Cmds) {
- var p = c as PredicateCmd;
- if (p != null && QKeyValue.FindBoolAttribute(p.Attributes, "start_checking_here")) {
- startPoints.Add(b);
- break;
- }
- }
- }
-
- // Compute the set of blocks reachable from blocks containing "start_checking_here"
- var blocksToCheck = new HashSet<Block>();
- foreach (var b in startPoints) {
- var todo = new Stack<Block>();
- var wasThere = blocksToCheck.Contains(b);
- todo.Push(b);
- while (todo.Count > 0) {
- var x = todo.Pop();
- if (blocksToCheck.Contains(x)) continue;
- blocksToCheck.Add(x);
- var ex = x.TransferCmd as GotoCmd;
- if (ex != null)
- foreach (Block e in ex.labelTargets)
- todo.Push(e);
- }
- if (!wasThere) blocksToCheck.Remove(b);
- }
-
- // Convert asserts to assumes in "unreachable" blocks, as well as in portions of blocks before we reach "start_checking_here"
- foreach (var b in impl.Blocks) {
- if (blocksToCheck.Contains(b)) continue; // All reachable blocks must be checked in their entirety, so don't change anything
- var newCmds = new List<Cmd>();
- var copyMode = false;
- foreach (Cmd c in b.Cmds) {
- var p = c as PredicateCmd;
- if (p != null && QKeyValue.FindBoolAttribute(p.Attributes, "start_checking_here"))
- copyMode = true;
- var asrt = c as AssertCmd;
- if (copyMode || asrt == null)
- newCmds.Add(c);
- else
- newCmds.Add(AssertTurnedIntoAssume(asrt));
- }
-
- b.Cmds = newCmds;
- }
- }
- }
-
- // Used by stratified inlining
- protected virtual void addExitAssert(string implName, Block exitBlock)
- {
- }
-
- public virtual Counterexample extractLoopTrace(Counterexample cex, string mainProcName, Program program, Dictionary<string, Dictionary<string, Block>> extractLoopMappingInfo)
- {
- // Construct the set of inlined procs in the original program
- var inlinedProcs = new HashSet<string>();
- foreach (var proc in program.Procedures)
- {
- if (!(proc is LoopProcedure))
- {
- inlinedProcs.Add(proc.Name);
- }
- }
-
- return extractLoopTraceRec(
- new CalleeCounterexampleInfo(cex, new List<object>()),
- mainProcName, inlinedProcs, extractLoopMappingInfo).counterexample;
- }
-
- protected CalleeCounterexampleInfo extractLoopTraceRec(
- CalleeCounterexampleInfo cexInfo, string currProc,
- HashSet<string> inlinedProcs,
- Dictionary<string, Dictionary<string, Block>> extractLoopMappingInfo)
- {
- Contract.Requires(currProc != null);
- if (cexInfo.counterexample == null) return cexInfo;
-
- var cex = cexInfo.counterexample;
- // Go through all blocks in the trace, map them back to blocks in the original program (if there is one)
- var ret = cex.Clone();
- ret.Trace = new List<Block>();
- ret.calleeCounterexamples = new Dictionary<TraceLocation, CalleeCounterexampleInfo>();
-
- for (int numBlock = 0; numBlock < cex.Trace.Count; numBlock ++ )
- {
- Block block = cex.Trace[numBlock];
- var origBlock = elGetBlock(currProc, block, extractLoopMappingInfo);
- if (origBlock != null) ret.Trace.Add(origBlock);
- var callCnt = 1;
- for (int numInstr = 0; numInstr < block.Cmds.Count; numInstr ++) {
- Cmd cmd = block.Cmds[numInstr];
- var loc = new TraceLocation(numBlock, numInstr);
- if (!cex.calleeCounterexamples.ContainsKey(loc))
- {
- if (getCallee(cex.getTraceCmd(loc), inlinedProcs) != null) callCnt++;
- continue;
- }
- string callee = cex.getCalledProcName(cex.getTraceCmd(loc));
- Contract.Assert(callee != null);
- var calleeTrace = cex.calleeCounterexamples[loc];
- Debug.Assert(calleeTrace != null);
-
- var origTrace = extractLoopTraceRec(calleeTrace, callee, inlinedProcs, extractLoopMappingInfo);
-
- if (elIsLoop(callee))
- {
- // Absorb the trace into the current trace
-
- int currLen = ret.Trace.Count;
- ret.Trace.AddRange(origTrace.counterexample.Trace);
-
- foreach (var kvp in origTrace.counterexample.calleeCounterexamples)
- {
- var newloc = new TraceLocation(kvp.Key.numBlock + currLen, kvp.Key.numInstr);
- ret.calleeCounterexamples.Add(newloc, kvp.Value);
- }
-
- }
- else
- {
- var origLoc = new TraceLocation(ret.Trace.Count - 1, getCallCmdPosition(origBlock, callCnt, inlinedProcs, callee));
- ret.calleeCounterexamples.Add(origLoc, origTrace);
- callCnt++;
- }
- }
- }
- return new CalleeCounterexampleInfo(ret, cexInfo.args);
- }
-
- // return the position of the i^th CallCmd in the block (count only those Calls that call a procedure in inlinedProcs).
- // Assert failure if there isn't any.
- // Assert that the CallCmd found calls "callee"
- private int getCallCmdPosition(Block block, int i, HashSet<string> inlinedProcs, string callee)
- {
- Debug.Assert(i >= 1);
- for (int pos = 0; pos < block.Cmds.Count; pos++)
- {
- Cmd cmd = block.Cmds[pos];
- string procCalled = getCallee(cmd, inlinedProcs);
-
- if (procCalled != null)
- {
- if (i == 1)
- {
- Debug.Assert(procCalled == callee);
- return pos;
- }
- i--;
- }
- }
-
- Debug.Assert(false, "Didn't find the i^th call cmd");
- return -1;
- }
-
- private string getCallee(Cmd cmd, HashSet<string> inlinedProcs)
- {
- string procCalled = null;
- if (cmd is CallCmd)
- {
- var cc = (CallCmd)cmd;
- if (inlinedProcs.Contains(cc.Proc.Name))
- {
- procCalled = cc.Proc.Name;
- }
- }
-
- if (cmd is AssumeCmd)
- {
- var expr = (cmd as AssumeCmd).Expr as NAryExpr;
- if (expr != null)
- {
- if (inlinedProcs.Contains(expr.Fun.FunctionName))
- {
- procCalled = expr.Fun.FunctionName;
- }
- }
- }
- return procCalled;
- }
-
- protected virtual bool elIsLoop(string procname)
- {
- return false;
- }
-
- private Block elGetBlock(string procname, Block block, Dictionary<string, Dictionary<string, Block>> extractLoopMappingInfo)
- {
- Contract.Requires(procname != null);
-
- if (!extractLoopMappingInfo.ContainsKey(procname))
- return block;
-
- if (!extractLoopMappingInfo[procname].ContainsKey(block.Label))
- return null;
-
- return extractLoopMappingInfo[procname][block.Label];
- }
-
- static Counterexample TraceCounterexample(
- Block/*!*/ b, Hashtable/*!*/ traceNodes, List<Block>/*!*/ trace, Model errModel, ModelViewInfo mvInfo,
- Dictionary<Incarnation, Absy/*!*/>/*!*/ incarnationOriginMap,
- ProverContext/*!*/ context,
- Dictionary<TraceLocation/*!*/, CalleeCounterexampleInfo/*!*/>/*!*/ calleeCounterexamples)
- {
- Contract.Requires(b != null);
- Contract.Requires(traceNodes != null);
- Contract.Requires(trace != null);
- Contract.Requires(cce.NonNullDictionaryAndValues(incarnationOriginMap));
- Contract.Requires(context != null);
- Contract.Requires(cce.NonNullDictionaryAndValues(calleeCounterexamples));
- // After translation, all potential errors come from asserts.
-
- while (true)
- {
- List<Cmd> cmds = b.Cmds;
- Contract.Assert(cmds != null);
- TransferCmd transferCmd = cce.NonNull(b.TransferCmd);
- for (int i = 0; i < cmds.Count; i++)
- {
- Cmd cmd = cce.NonNull(cmds[i]);
-
- // Skip if 'cmd' not contained in the trace or not an assert
- if (cmd is AssertCmd && traceNodes.Contains(cmd))
- {
- Counterexample newCounterexample = AssertCmdToCounterexample((AssertCmd)cmd, transferCmd, trace, errModel, mvInfo, context);
- Contract.Assert(newCounterexample != null);
- newCounterexample.AddCalleeCounterexample(calleeCounterexamples);
- return newCounterexample;
- }
- }
-
- GotoCmd gotoCmd = transferCmd as GotoCmd;
- if (gotoCmd == null) return null;
- Block foundBlock = null;
- foreach (Block bb in cce.NonNull(gotoCmd.labelTargets))
- {
- Contract.Assert(bb != null);
- if (traceNodes.Contains(bb))
- {
- foundBlock = bb;
- break;
- }
- }
- if (foundBlock == null) return null;
- trace.Add(foundBlock);
- b = foundBlock;
- }
- }
-
- public static Counterexample AssertCmdToCounterexample(AssertCmd cmd, TransferCmd transferCmd, List<Block> trace, Model errModel, ModelViewInfo mvInfo, ProverContext context)
- {
- Contract.Requires(cmd != null);
- Contract.Requires(transferCmd != null);
- Contract.Requires(trace != null);
- Contract.Requires(context != null);
- Contract.Ensures(Contract.Result<Counterexample>() != null);
-
- List<string> relatedInformation = new List<string>();
-
- // See if it is a special assert inserted in translation
- if (cmd is AssertRequiresCmd)
- {
- AssertRequiresCmd assertCmd = (AssertRequiresCmd)cmd;
- Contract.Assert(assertCmd != null);
- CallCounterexample cc = new CallCounterexample(trace, assertCmd.Call, assertCmd.Requires, errModel, mvInfo, context, assertCmd.Checksum);
- cc.relatedInformation = relatedInformation;
- return cc;
- }
- else if (cmd is AssertEnsuresCmd)
- {
- AssertEnsuresCmd assertCmd = (AssertEnsuresCmd)cmd;
- Contract.Assert(assertCmd != null);
- ReturnCounterexample rc = new ReturnCounterexample(trace, transferCmd, assertCmd.Ensures, errModel, mvInfo, context, cmd.Checksum);
- rc.relatedInformation = relatedInformation;
- return rc;
- }
- else
- {
- AssertCounterexample ac = new AssertCounterexample(trace, (AssertCmd)cmd, errModel, mvInfo, context);
- ac.relatedInformation = relatedInformation;
- return ac;
- }
- }
-
- static VCExpr LetVC(Block startBlock,
- VCExpr controlFlowVariableExpr,
- Dictionary<int, Absy> label2absy,
- ProverContext proverCtxt,
- out int assertionCount) {
- Contract.Requires(startBlock != null);
- Contract.Requires(proverCtxt != null);
-
- Contract.Ensures(Contract.Result<VCExpr>() != null);
-
- Hashtable/*<Block, LetVariable!>*/ blockVariables = new Hashtable/*<Block, LetVariable!!>*/();
- List<VCExprLetBinding> bindings = new List<VCExprLetBinding>();
- VCExpr startCorrect = LetVC(startBlock, controlFlowVariableExpr, label2absy, blockVariables, bindings, proverCtxt, out assertionCount);
- return proverCtxt.ExprGen.Let(bindings, startCorrect);
- }
-
- static VCExpr LetVCIterative(List<Block> blocks,
- VCExpr controlFlowVariableExpr,
- Dictionary<int, Absy> label2absy,
- ProverContext proverCtxt,
- out int assertionCount,
- bool isPositiveContext = true)
- {
- Contract.Requires(blocks != null);
- Contract.Requires(proverCtxt != null);
- Contract.Ensures(Contract.Result<VCExpr>() != null);
-
- assertionCount = 0;
-
- Graph<Block> dag = new Graph<Block>();
- dag.AddSource(blocks[0]);
- foreach (Block b in blocks) {
- GotoCmd gtc = b.TransferCmd as GotoCmd;
- if (gtc != null) {
- Contract.Assume(gtc.labelTargets != null);
- foreach (Block dest in gtc.labelTargets) {
- Contract.Assert(dest != null);
- dag.AddEdge(dest, b);
- }
- }
- }
- IEnumerable sortedNodes = dag.TopologicalSort();
- Contract.Assert(sortedNodes != null);
-
- Dictionary<Block, VCExprVar> blockVariables = new Dictionary<Block, VCExprVar>();
- List<VCExprLetBinding> bindings = new List<VCExprLetBinding>();
- VCExpressionGenerator gen = proverCtxt.ExprGen;
- Contract.Assert(gen != null);
- foreach (Block block in sortedNodes) {
- VCExpr SuccCorrect;
- GotoCmd gotocmd = block.TransferCmd as GotoCmd;
- if (gotocmd == null) {
- ReturnExprCmd re = block.TransferCmd as ReturnExprCmd;
- if (re == null) {
- SuccCorrect = VCExpressionGenerator.True;
- }
- else {
- SuccCorrect = proverCtxt.BoogieExprTranslator.Translate(re.Expr);
- if (isPositiveContext)
- {
- SuccCorrect = gen.Not(SuccCorrect);
- }
- }
- }
- else {
- Contract.Assert(gotocmd.labelTargets != null);
- List<VCExpr> SuccCorrectVars = new List<VCExpr>(gotocmd.labelTargets.Count);
- foreach (Block successor in gotocmd.labelTargets) {
- Contract.Assert(successor != null);
- VCExpr s = blockVariables[successor];
- if (controlFlowVariableExpr != null) {
- VCExpr controlFlowFunctionAppl = gen.ControlFlowFunctionApplication(controlFlowVariableExpr, gen.Integer(BigNum.FromInt(block.UniqueId)));
- VCExpr controlTransferExpr = gen.Eq(controlFlowFunctionAppl, gen.Integer(BigNum.FromInt(successor.UniqueId)));
- s = gen.Implies(controlTransferExpr, s);
- }
- SuccCorrectVars.Add(s);
- }
- SuccCorrect = gen.NAry(VCExpressionGenerator.AndOp, SuccCorrectVars);
- }
-
- VCContext context = new VCContext(label2absy, proverCtxt, controlFlowVariableExpr, isPositiveContext);
- VCExpr vc = Wlp.Block(block, SuccCorrect, context);
- assertionCount += context.AssertionCount;
-
- VCExprVar v = gen.Variable(block.Label + "_correct", Bpl.Type.Bool);
- bindings.Add(gen.LetBinding(v, vc));
- blockVariables.Add(block, v);
- }
-
- return proverCtxt.ExprGen.Let(bindings, blockVariables[blocks[0]]);
- }
-
- static VCExpr LetVC(Block block,
- VCExpr controlFlowVariableExpr,
- Dictionary<int, Absy> label2absy,
- Hashtable/*<Block, VCExprVar!>*/ blockVariables,
- List<VCExprLetBinding/*!*/>/*!*/ bindings,
- ProverContext proverCtxt,
- out int assertionCount)
- {
- Contract.Requires(block != null);
- Contract.Requires(blockVariables!= null);
- Contract.Requires(cce.NonNullElements(bindings));
- Contract.Requires(proverCtxt != null);
-
- Contract.Ensures(Contract.Result<VCExpr>() != null);
-
- assertionCount = 0;
-
- VCExpressionGenerator gen = proverCtxt.ExprGen;
- Contract.Assert(gen != null);
- VCExprVar v = (VCExprVar)blockVariables[block];
- if (v == null) {
- /*
- * For block A (= block), generate:
- * LET_binding A_correct = wp(A_body, (/\ S \in Successors(A) :: S_correct))
- * with the side effect of adding the let bindings to "bindings" for any
- * successor not yet visited.
- */
- VCExpr SuccCorrect;
- GotoCmd gotocmd = block.TransferCmd as GotoCmd;
- if (gotocmd == null) {
- ReturnExprCmd re = block.TransferCmd as ReturnExprCmd;
- if (re == null) {
- SuccCorrect = VCExpressionGenerator.True;
- } else {
- SuccCorrect = proverCtxt.BoogieExprTranslator.Translate(re.Expr);
- }
- } else {
- Contract.Assert( gotocmd.labelTargets != null);
- List<VCExpr> SuccCorrectVars = new List<VCExpr>(gotocmd.labelTargets.Count);
- foreach (Block successor in gotocmd.labelTargets) {
- Contract.Assert(successor != null);
- int ac;
- VCExpr s = LetVC(successor, controlFlowVariableExpr, label2absy, blockVariables, bindings, proverCtxt, out ac);
- assertionCount += ac;
- if (controlFlowVariableExpr != null)
- {
- VCExpr controlFlowFunctionAppl = gen.ControlFlowFunctionApplication(controlFlowVariableExpr, gen.Integer(BigNum.FromInt(block.UniqueId)));
- VCExpr controlTransferExpr = gen.Eq(controlFlowFunctionAppl, gen.Integer(BigNum.FromInt(successor.UniqueId)));
- s = gen.Implies(controlTransferExpr, s);
- }
- SuccCorrectVars.Add(s);
- }
- SuccCorrect = gen.NAry(VCExpressionGenerator.AndOp, SuccCorrectVars);
- }
-
-
- VCContext context = new VCContext(label2absy, proverCtxt, controlFlowVariableExpr);
- VCExpr vc = Wlp.Block(block, SuccCorrect, context);
- assertionCount += context.AssertionCount;
-
- v = gen.Variable(block.Label + "_correct", Bpl.Type.Bool);
- bindings.Add(gen.LetBinding(v, vc));
- blockVariables.Add(block, v);
- }
- return v;
- }
-
- static VCExpr DagVC(Block block,
- VCExpr controlFlowVariableExpr,
- Dictionary<int, Absy> label2absy,
- Hashtable/*<Block, VCExpr!>*/ blockEquations,
- ProverContext proverCtxt,
- out int assertionCount)
- {
- Contract.Requires(block != null);
- Contract.Requires(label2absy != null);
- Contract.Requires(blockEquations != null);
- Contract.Requires(proverCtxt != null);
- Contract.Ensures(Contract.Result<VCExpr>() != null);
-
- assertionCount = 0;
- VCExpressionGenerator gen = proverCtxt.ExprGen;
- Contract.Assert(gen != null);
- VCExpr vc = (VCExpr)blockEquations[block];
- if (vc != null) {
- return vc;
- }
-
- /*
- * For block A (= block), generate:
- * wp(A_body, (/\ S \in Successors(A) :: DagVC(S)))
- */
- VCExpr SuccCorrect = null;
- GotoCmd gotocmd = block.TransferCmd as GotoCmd;
- if (gotocmd != null)
- {
- foreach (Block successor in cce.NonNull(gotocmd.labelTargets)) {
- Contract.Assert(successor != null);
- int ac;
- VCExpr c = DagVC(successor, controlFlowVariableExpr, label2absy, blockEquations, proverCtxt, out ac);
- assertionCount += ac;
- if (controlFlowVariableExpr != null) {
- VCExpr controlFlowFunctionAppl = gen.ControlFlowFunctionApplication(controlFlowVariableExpr, gen.Integer(BigNum.FromInt(block.UniqueId)));
- VCExpr controlTransferExpr = gen.Eq(controlFlowFunctionAppl, gen.Integer(BigNum.FromInt(successor.UniqueId)));
- c = gen.Implies(controlTransferExpr, c);
- }
- SuccCorrect = SuccCorrect == null ? c : gen.And(SuccCorrect, c);
- }
- }
- if (SuccCorrect == null) {
- SuccCorrect = VCExpressionGenerator.True;
- }
-
- VCContext context = new VCContext(label2absy, proverCtxt, controlFlowVariableExpr);
- vc = Wlp.Block(block, SuccCorrect, context);
- assertionCount += context.AssertionCount;
-
- // gen.MarkAsSharedFormula(vc); PR: don't know yet what to do with this guy
-
- blockEquations.Add(block, vc);
- return vc;
- }
-
- static VCExpr FlatBlockVC(Implementation impl,
- Dictionary<int, Absy> label2absy,
- bool local, bool reach, bool doomed,
- ProverContext proverCtxt,
- out int assertionCount)
- {
- Contract.Requires(impl != null);
- Contract.Requires(label2absy != null);
- Contract.Requires(proverCtxt != null);
- Contract.Requires( !local || !reach); // "reach" must be false for local
-
- VCExpressionGenerator gen = proverCtxt.ExprGen;
- Contract.Assert(gen != null);
- Hashtable/* Block --> VCExprVar */ BlkCorrect = BlockVariableMap(impl.Blocks, "_correct", gen);
- Hashtable/* Block --> VCExprVar */ BlkReached = reach ? BlockVariableMap(impl.Blocks, "_reached", gen) : null;
-
- List<Block> blocks = impl.Blocks;
- Contract.Assert(blocks != null);
- // block sorting is now done on the VCExpr
- // if (!local && (cce.NonNull(CommandLineOptions.Clo.TheProverFactory).NeedsBlockSorting) {
- // blocks = SortBlocks(blocks);
- // }
-
- VCExpr proofObligation;
- if (!local) {
- proofObligation = cce.NonNull((VCExprVar)BlkCorrect[impl.Blocks[0]]);
- } else {
- List<VCExpr> conjuncts = new List<VCExpr>(blocks.Count);
- foreach (Block b in blocks) {Contract.Assert(b != null);
- VCExpr v = cce.NonNull((VCExprVar)BlkCorrect[b]);
- conjuncts.Add(v);
- }
- proofObligation = gen.NAry(VCExpressionGenerator.AndOp, conjuncts);
- }
-
- VCContext context = new VCContext(label2absy, proverCtxt);
- Contract.Assert(context != null);
-
- List<VCExprLetBinding> programSemantics = new List<VCExprLetBinding>(blocks.Count);
- foreach (Block b in blocks) {Contract.Assert(b != null);
- /*
- * In block mode,
- * For a return block A, generate:
- * A_correct <== wp(A_body, true) [post-condition has been translated into an assert]
- * For all other blocks, generate:
- * A_correct <== wp(A_body, (/\ S \in Successors(A) :: S_correct))
- *
- * In doomed mode, proceed as in block mode, except for a return block A, generate:
- * A_correct <== wp(A_body, false) [post-condition has been translated into an assert]
- *
- * In block reach mode, the wp(A_body,...) in the equations above change to:
- * A_reached ==> wp(A_body,...)
- * and the conjunction above changes to:
- * (/\ S \in Successors(A) :: S_correct \/ (\/ T \in Successors(A) && T != S :: T_reached))
- *
- * In local mode, generate:
- * A_correct <== wp(A_body, true)
- */
- VCExpr SuccCorrect;
- if (local) {
- SuccCorrect = VCExpressionGenerator.True;
- } else {
- SuccCorrect = SuccessorsCorrect(b, BlkCorrect, BlkReached, doomed, gen);
- }
-
- VCExpr wlp = Wlp.Block(b, SuccCorrect, context);
- if (BlkReached != null) {
- wlp = gen.Implies(cce.NonNull((VCExprVar)BlkReached[b]), wlp);
- }
-
- VCExprVar okVar = cce.NonNull((VCExprVar)BlkCorrect[b]);
- VCExprLetBinding binding = gen.LetBinding(okVar, wlp);
- programSemantics.Add(binding);
- }
-
- assertionCount = context.AssertionCount;
- return gen.Let(programSemantics, proofObligation);
- }
-
- private static Hashtable/* Block --> VCExprVar */ BlockVariableMap(List<Block/*!*/>/*!*/ blocks, string suffix,
- Microsoft.Boogie.VCExpressionGenerator gen) {
- Contract.Requires(cce.NonNullElements(blocks));
- Contract.Requires(suffix != null);
- Contract.Requires(gen != null);
- Contract.Ensures(Contract.Result<Hashtable>() != null);
-
- Hashtable/* Block --> VCExprVar */ map = new Hashtable/* Block --> (Let)Variable */();
- foreach (Block b in blocks) {
- Contract.Assert(b != null);
- VCExprVar v = gen.Variable(b.Label + suffix, Bpl.Type.Bool);
- Contract.Assert(v != null);
- map.Add(b, v);
- }
- return map;
- }
-
- private static VCExpr SuccessorsCorrect(
- Block b,
- Hashtable/* Block --> VCExprVar */ BlkCorrect,
- Hashtable/* Block --> VCExprVar */ BlkReached,
- bool doomed,
- Microsoft.Boogie.VCExpressionGenerator gen) {
- Contract.Requires(b != null);
- Contract.Requires(BlkCorrect != null);
- Contract.Requires(gen != null);
- Contract.Ensures(Contract.Result<VCExpr>() != null);
-
- VCExpr SuccCorrect = null;
- GotoCmd gotocmd = b.TransferCmd as GotoCmd;
- if (gotocmd != null) {
- foreach (Block successor in cce.NonNull(gotocmd.labelTargets)) {
- Contract.Assert(successor != null);
- // c := S_correct
- VCExpr c = (VCExprVar)BlkCorrect[successor];
- Contract.Assert(c != null);
- if (BlkReached != null) {
- // c := S_correct \/ Sibling0_reached \/ Sibling1_reached \/ ...;
- foreach (Block successorSibling in gotocmd.labelTargets) {
- Contract.Assert(successorSibling != null);
- if (successorSibling != successor) {
- c = gen.Or(c, cce.NonNull((VCExprVar)BlkReached[successorSibling]));
- }
- }
- }
- SuccCorrect = SuccCorrect == null ? c : gen.And(SuccCorrect, c);
- }
- }
- if (SuccCorrect == null) {
- return VCExpressionGenerator.True;
- } else if (doomed) {
- return VCExpressionGenerator.False;
- } else {
- return SuccCorrect;
- }
- }
-
- static VCExpr NestedBlockVC(Implementation impl,
- Dictionary<int, Absy> label2absy,
- bool reach,
- ProverContext proverCtxt,
- out int assertionCount){
- Contract.Requires(impl != null);
- Contract.Requires(label2absy != null);
- Contract.Requires(proverCtxt != null);
- Contract.Requires( impl.Blocks.Count != 0);
- Contract.Ensures(Contract.Result<VCExpr>() != null);
-
- VCExpressionGenerator gen = proverCtxt.ExprGen;
- Contract.Assert(gen != null);
- Graph<Block> g = Program.GraphFromImpl(impl);
-
- Hashtable/* Block --> VCExprVar */ BlkCorrect = BlockVariableMap(impl.Blocks, "_correct", gen);
- Hashtable/* Block --> VCExprVar */ BlkReached = reach ? BlockVariableMap(impl.Blocks, "_reached", gen) : null;
-
- Block startBlock = cce.NonNull( impl.Blocks[0]);
- VCExpr proofObligation = (VCExprVar)BlkCorrect[startBlock];
- Contract.Assert(proofObligation != null);
- VCContext context = new VCContext(label2absy, proverCtxt);
-
- Hashtable/*Block->int*/ totalOrder = new Hashtable/*Block->int*/();
- {
- List<Block> blocks = impl.Blocks;
-
- // block sorting is now done on the VCExpr
- // if (((!)CommandLineOptions.Clo.TheProverFactory).NeedsBlockSorting) {
- // blocks = SortBlocks(blocks);
- // }
- int i = 0;
- foreach (Block b in blocks) {
- Contract.Assert(b != null);
- totalOrder[b] = i;
- i++;
- }
- }
-
- VCExprLetBinding programSemantics = NestedBlockEquation(cce.NonNull(impl.Blocks[0]), BlkCorrect, BlkReached, totalOrder, context, g, gen);
- List<VCExprLetBinding> ps = new List<VCExprLetBinding>(1);
- ps.Add(programSemantics);
-
- assertionCount = context.AssertionCount;
- return gen.Let(ps, proofObligation);
- }
-
- private static VCExprLetBinding NestedBlockEquation(Block b,
- Hashtable/*Block-->VCExprVar*/ BlkCorrect,
- Hashtable/*Block-->VCExprVar*/ BlkReached,
- Hashtable/*Block->int*/ totalOrder,
- VCContext context,
- Graph<Block> g,
- Microsoft.Boogie.VCExpressionGenerator gen) {
- Contract.Requires(b != null);
- Contract.Requires(BlkCorrect != null);
- Contract.Requires(totalOrder != null);
- Contract.Requires(g != null);
- Contract.Requires(context != null);
-
- Contract.Ensures(Contract.Result<VCExprLetBinding>() != null);
-
- /*
- * For a block b, return:
- * LET_BINDING b_correct = wp(b_body, X)
- * where X is:
- * LET (THOSE d \in DirectDominates(b) :: BlockEquation(d))
- * IN (/\ s \in Successors(b) :: s_correct)
- *
- * When the VC-expression generator does not support LET expresions, this
- * will eventually turn into:
- * b_correct <== wp(b_body, X)
- * where X is:
- * (/\ s \in Successors(b) :: s_correct)
- * <==
- * (/\ d \in DirectDominatees(b) :: BlockEquation(d))
- *
- * In both cases above, if BlkReached is non-null, then the wp expression
- * is instead:
- * b_reached ==> wp(b_body, X)
- */
-
- VCExpr SuccCorrect = SuccessorsCorrect(b, BlkCorrect, null, false, gen);
- Contract.Assert(SuccCorrect != null);
-
- List<VCExprLetBinding> bindings = new List<VCExprLetBinding>();
- foreach (Block dominee in GetSortedBlocksImmediatelyDominatedBy(g, b, totalOrder)) {
- Contract.Assert(dominee != null);
- VCExprLetBinding c = NestedBlockEquation(dominee, BlkCorrect, BlkReached, totalOrder, context, g, gen);
- bindings.Add(c);
- }
-
- VCExpr X = gen.Let(bindings, SuccCorrect);
- VCExpr wlp = Wlp.Block(b, X, context);
- if (BlkReached != null) {
- wlp = gen.Implies((VCExprVar)BlkReached[b], wlp);
- Contract.Assert(wlp != null);
- }
- VCExprVar okVar = cce.NonNull((VCExprVar)BlkCorrect[b]);
- return gen.LetBinding(okVar, wlp);
- }
-
- /// <summary>
- /// Returns a list of g.ImmediatelyDominatedBy(b), but in a sorted order, hoping to steer around
- /// the nondeterminism problems we've been seeing by using just this call.
- /// </summary>
- static List<Block/*!*/>/*!*/ GetSortedBlocksImmediatelyDominatedBy(Graph<Block>/*!*/ g, Block/*!*/ b, Hashtable/*Block->int*//*!*/ totalOrder) {
- Contract.Requires(g != null);
- Contract.Requires(b != null);
- Contract.Requires(totalOrder != null);
- Contract.Ensures(Contract.Result<List<Block>>() != null);
-
- List<Block> list = new List<Block>();
- foreach (Block dominee in g.ImmediatelyDominatedBy(b)) {
- Contract.Assert(dominee != null);
- list.Add(dominee);
- }
- list.Sort(new Comparison<Block>(delegate(Block x, Block y) {
- return (int)cce.NonNull(totalOrder[x]) - (int)cce.NonNull(totalOrder[y]);
- }));
- return list;
- }
-
- static VCExpr VCViaStructuredProgram
- (Implementation impl, Dictionary<int, Absy> label2absy,
- ProverContext proverCtxt,
- out int assertionCount)
- {
- Contract.Requires(impl != null);
- Contract.Requires(label2absy != null);
- Contract.Requires(proverCtxt != null);
- Contract.Ensures(Contract.Result<VCExpr>() != null);
-
- #region Convert block structure back to a "regular expression"
- RE r = DAG2RE.Transform(cce.NonNull(impl.Blocks[0]));
- Contract.Assert(r != null);
- #endregion
-
- VCContext ctxt = new VCContext(label2absy, proverCtxt);
- Contract.Assert(ctxt != null);
- #region Send wlp(program,true) to Simplify
- var vcexp = Wlp.RegExpr(r, VCExpressionGenerator.True, ctxt);
- assertionCount = ctxt.AssertionCount;
- return vcexp;
- #endregion
- }
-
- /// <summary>
- /// Remove empty blocks reachable from the startBlock of the CFG
- /// </summary>
- static void RemoveEmptyBlocksIterative(List<Block> blocks) {
- // postorder traversal of cfg
- // noting loop heads in [keep] and
- // generating token information in [renameInfo]
- Block startBlock = blocks[0];
- var postorder = new List<Block>();
- var keep = new HashSet<Block>();
- var visited = new HashSet<Block>();
- var grey = new HashSet<Block>();
- var stack = new Stack<Block>();
- Dictionary<Block, Block> renameInfo = new Dictionary<Block, Block>();
-
- stack.Push(startBlock);
- visited.Add(startBlock);
- while (stack.Count != 0) {
- var curr = stack.Pop();
- if (grey.Contains(curr)) {
- postorder.Add(curr);
-
- // generate renameInfoForStartBlock
- GotoCmd gtc = curr.TransferCmd as GotoCmd;
- renameInfo[curr] = null;
- if (gtc == null || gtc.labelTargets == null || gtc.labelTargets.Count == 0) {
- if (curr.Cmds.Count == 0 && curr.tok.IsValid) {
- renameInfo[curr] = curr;
- }
- } else {
- if (curr.Cmds.Count == 0 || curr == startBlock) {
- if (curr.tok.IsValid) {
- renameInfo[curr] = curr;
- } else {
- HashSet<Block> successorRenameInfo = new HashSet<Block>();
- foreach (Block s in gtc.labelTargets) {
- if (keep.Contains(s)) {
- successorRenameInfo.Add(null);
- } else {
- successorRenameInfo.Add(renameInfo[s]);
- }
- }
- if (successorRenameInfo.Count == 1) {
- renameInfo[curr] = successorRenameInfo.Single();
- }
- }
- }
- }
- // end generate renameInfoForStartBlock
-
- } else {
- grey.Add(curr);
- stack.Push(curr);
- GotoCmd gtc = curr.TransferCmd as GotoCmd;
- if (gtc == null || gtc.labelTargets == null || gtc.labelTargets.Count == 0) continue;
- foreach (Block s in gtc.labelTargets) {
- if (!visited.Contains(s)) {
- visited.Add(s);
- stack.Push(s);
- } else if (grey.Contains(s) && !postorder.Contains(s)) { // s is a loop head
- keep.Add(s);
- }
- }
- }
- }
- keep.Add(startBlock);
-
- foreach (Block b in postorder) {
- if (!keep.Contains(b) && b.Cmds.Count == 0) {
- GotoCmd bGtc = b.TransferCmd as GotoCmd;
- foreach (Block p in b.Predecessors) {
- GotoCmd pGtc = p.TransferCmd as GotoCmd;
- Contract.Assert(pGtc != null);
- pGtc.labelTargets.Remove(b);
- pGtc.labelNames.Remove(b.Label);
- }
- if (bGtc == null || bGtc.labelTargets == null || bGtc.labelTargets.Count == 0) {
- continue;
- }
-
- List<Block> successors = bGtc.labelTargets;
-
- // Try to push token information if possible
- if (b.tok.IsValid && successors.Count == 1 && b != renameInfo[startBlock]) {
- var s = successors.Single();
- if (!s.tok.IsValid) {
- foreach (Block p in s.Predecessors) {
- if (p != b) {
- GotoCmd pGtc = p.TransferCmd as GotoCmd;
- Contract.Assert(pGtc != null);
- pGtc.labelTargets.Remove(s);
- pGtc.labelNames.Remove(s.Label);
- pGtc.labelTargets.Add(s);
- pGtc.labelNames.Add(b.Label);
- }
- }
- s.tok = b.tok;
- s.Label = b.Label;
- }
- }
-
- foreach (Block p in b.Predecessors) {
- GotoCmd pGtc = p.TransferCmd as GotoCmd;
- Contract.Assert(pGtc != null);
- foreach (Block s in successors) {
- if (!pGtc.labelTargets.Contains(s)) {
- pGtc.labelTargets.Add(s);
- pGtc.labelNames.Add(s.Label);
- }
- }
- }
- }
- }
-
- if (!startBlock.tok.IsValid && startBlock.Cmds.All(c => c is AssumeCmd)) {
- if (renameInfo[startBlock] != null) {
- startBlock.tok = renameInfo[startBlock].tok;
- startBlock.Label = renameInfo[startBlock].Label;
- }
- }
-
- }
-
- /// <summary>
- /// Remove the empty blocks reachable from the block.
- /// It changes the visiting state of the blocks, so that if you want to visit again the blocks, you have to reset them...
- /// </summary>
- static List<Block> RemoveEmptyBlocks(Block b) {
- Contract.Requires(b != null);
- Contract.Ensures(Contract.Result<List<Block>>() != null);
-
- Contract.Assert(b.TraversingStatus == Block.VisitState.ToVisit);
- Block renameInfo;
- List<Block> retVal = removeEmptyBlocksWorker(b, true, out renameInfo);
- if (renameInfo != null && !b.tok.IsValid) {
- bool onlyAssumes = true;
- foreach (Cmd c in b.Cmds) {
- if (!(c is AssumeCmd)) {
- onlyAssumes = false;
- break;
- }
- }
- if (onlyAssumes) {
- b.tok = renameInfo.tok;
- b.Label = renameInfo.Label;
- }
- }
- return retVal;
- }
-
- /// <summary>
- /// For every not-yet-visited block n reachable from b, change n's successors to skip empty nodes.
- /// Return the *set* of blocks reachable from b without passing through a nonempty block.
- /// The target of any backedge is counted as a nonempty block.
- /// If renameInfoForStartBlock is non-null, it denotes an empty block with location information, and that
- /// information would be appropriate to display
- /// </summary>
- private static List<Block> removeEmptyBlocksWorker(Block b, bool startNode, out Block renameInfoForStartBlock)
- {
- Contract.Requires(b != null);
- Contract.Ensures(Contract.ValueAtReturn(out renameInfoForStartBlock) == null || Contract.ValueAtReturn(out renameInfoForStartBlock).tok.IsValid);
- // ensures: b in result ==> renameInfoForStartBlock == null;
-
- renameInfoForStartBlock = null;
- List<Block> bs = new List<Block>();
- GotoCmd gtc = b.TransferCmd as GotoCmd;
-
- // b has no successors
- if (gtc == null || gtc.labelTargets == null || gtc.labelTargets.Count == 0)
- {
- if (b.Cmds.Count != 0){ // only empty blocks are removed...
- bs.Add(b);
- } else if (b.tok.IsValid) {
- renameInfoForStartBlock = b;
- }
- return bs;
- }
- else if (b.TraversingStatus == Block.VisitState.ToVisit) // if b has some successors and we have not seen it so far...
- {
- b.TraversingStatus = Block.VisitState.BeingVisited;
-
- // Before recursing down to successors, make a sobering observation:
- // If b has no commands and is not the start node, then it will see
- // extinction (because it will not be included in the "return setOfSuccessors"
- // statement below). In that case, if b has a location, then the location
- // information would be lost. Hence, make an attempt to save the location
- // by pushing the location onto b's successor. This can be done if (0) b has
- // exactly one successor, (1) that successor has no location of its own, and
- // (2) that successor has no other predecessors.
- if (b.Cmds.Count == 0 && !startNode) {
- // b is about to become extinct; try to save its name and location, if possible
- if (b.tok.IsValid && gtc.labelTargets.Count == 1) {
- Block succ = cce.NonNull(gtc.labelTargets[0]);
- if (!succ.tok.IsValid && succ.Predecessors.Count == 1) {
- succ.tok = b.tok;
- succ.Label = b.Label;
- }
- }
- }
-
- // recursively call this method on each successor
- // merge result into a *set* of blocks
- HashSet<Block> mergedSuccessors = new HashSet<Block>();
- int m = 0; // in the following loop, set renameInfoForStartBlock to the value that all recursive calls agree on, if possible; otherwise, null
- foreach (Block dest in gtc.labelTargets){Contract.Assert(dest != null);
- Block renameInfo;
- List<Block> ys = removeEmptyBlocksWorker(dest, false, out renameInfo);
- Contract.Assert(ys != null);
- if (m == 0) {
- renameInfoForStartBlock = renameInfo;
- } else if (renameInfoForStartBlock != renameInfo) {
- renameInfoForStartBlock = null;
- }
- foreach (Block successor in ys){
- if (!mergedSuccessors.Contains(successor))
- mergedSuccessors.Add(successor);
- }
- m++;
- }
- b.TraversingStatus = Block.VisitState.AlreadyVisited;
-
- List<Block> setOfSuccessors = new List<Block>();
- foreach (Block d in mergedSuccessors)
- setOfSuccessors.Add(d);
- if (b.Cmds.Count == 0 && !startNode) {
- // b is about to become extinct
- if (b.tok.IsValid) {
- renameInfoForStartBlock = b;
- }
- return setOfSuccessors;
- }
- // otherwise, update the list of successors of b to be the blocks in setOfSuccessors
- gtc.labelTargets = setOfSuccessors;
- gtc.labelNames = new List<String>();
- foreach (Block d in setOfSuccessors){
- Contract.Assert(d != null);
- gtc.labelNames.Add(d.Label);}
- if (!startNode) {
- renameInfoForStartBlock = null;
- }
- return new List<Block> { b };
- }
- else // b has some successors, but we are already visiting it, or we have already visited it...
- {
- return new List<Block> { b };
- }
- }
-
- static void DumpMap(Hashtable /*Variable->Expr*/ map) {
- Contract.Requires(map != null);
- foreach (DictionaryEntry de in map) {
- Variable v = (Variable)de.Key;
- Contract.Assert(v != null);
- Expr e = (Expr)de.Value;
- Contract.Assert(e != null);
- Console.Write(" ");
- v.Emit(new TokenTextWriter("<console>", Console.Out, /*setTokens=*/ false, /*pretty=*/ false), 0);
- Console.Write(" --> ");
- e.Emit(new TokenTextWriter("<console>", Console.Out, /*setTokens=*/ false, /*pretty=*/ false));
- Console.WriteLine();
- }
- }
- }
-}
+//----------------------------------------------------------------------------- +// +// Copyright (C) Microsoft Corporation. All Rights Reserved. +// +//----------------------------------------------------------------------------- +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.IO; +using Microsoft.Boogie; +using Microsoft.Boogie.GraphUtil; +using System.Diagnostics.Contracts; +using Microsoft.Basetypes; +using Microsoft.Boogie.VCExprAST; + +namespace VC { + using Bpl = Microsoft.Boogie; + using System.Threading.Tasks; + + public class VCGen : ConditionGeneration { + private const bool _print_time = false; + /// <summary> + /// Constructor. Initializes the theorem prover. + /// </summary> + [NotDelayed] + public VCGen(Program program, string/*?*/ logFilePath, bool appendLogFile, List<Checker> checkers) + : base(program, checkers) + { + Contract.Requires(program != null); + this.appendLogFile = appendLogFile; + this.logFilePath = logFilePath; + } + + private static AssumeCmd AssertTurnedIntoAssume(AssertCmd assrt) { + Contract.Requires(assrt != null); + Contract.Ensures(Contract.Result<AssumeCmd>() != null); + + Expr expr = assrt.Expr; + Contract.Assert(expr != null); + switch (Wlp.Subsumption(assrt)) { + case CommandLineOptions.SubsumptionOption.Never: + expr = Expr.True; + break; + case CommandLineOptions.SubsumptionOption.Always: + break; + case CommandLineOptions.SubsumptionOption.NotForQuantifiers: + if (expr is QuantifierExpr) { + expr = Expr.True; + } + break; + default: + Contract.Assert(false); + throw new cce.UnreachableException(); // unexpected case + } + + return new AssumeCmd(assrt.tok, expr); + } + + #region Soundness smoke tester + class SmokeTester { + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(parent != null); + Contract.Invariant(impl != null); + Contract.Invariant(initial != null); + Contract.Invariant(cce.NonNullDictionaryAndValues(copies)); + Contract.Invariant(cce.NonNull(visited)); + Contract.Invariant(callback != null); + } + + VCGen parent; + Implementation impl; + Block initial; + int id; + Dictionary<Block, Block> copies = new Dictionary<Block, Block>(); + HashSet<Block> visited = new HashSet<Block>(); + VerifierCallback callback; + + internal SmokeTester(VCGen par, Implementation i, VerifierCallback callback) { + Contract.Requires(par != null); + Contract.Requires(i != null); + Contract.Requires(callback != null); + parent = par; + impl = i; + initial = i.Blocks[0]; + this.callback = callback; + } + + internal void Copy() { + CloneBlock(impl.Blocks[0]); + initial = GetCopiedBlocks()[0]; + } + + internal void Test() { + Contract.EnsuresOnThrow<UnexpectedProverOutputException>(true); + + DFS(initial); + } + + void TopologicalSortImpl() { + Graph<Block> dag = new Graph<Block>(); + dag.AddSource(cce.NonNull(impl.Blocks[0])); // there is always at least one node in the graph + foreach (Block b in impl.Blocks) { + GotoCmd gtc = b.TransferCmd as GotoCmd; + if (gtc != null) { + Contract.Assume(gtc.labelTargets != null); + foreach (Block dest in gtc.labelTargets) { + Contract.Assert(dest != null); + dag.AddEdge(b, dest); + } + } + } + impl.Blocks = new List<Block>(); + foreach (Block b in dag.TopologicalSort()) { + Contract.Assert(b != null); + impl.Blocks.Add(b); + } + } + + void Emit() { + TopologicalSortImpl(); + EmitImpl(impl, false); + } + + // this one copies forward + Block CloneBlock(Block b) { + Contract.Requires(b != null); + Contract.Ensures(Contract.Result<Block>() != null); + + Block fake_res; + if (copies.TryGetValue(b, out fake_res)) { + return cce.NonNull(fake_res); + } + Block res = new Block(b.tok, b.Label, new List<Cmd>(b.Cmds), null); + copies[b] = res; + if (b.TransferCmd is GotoCmd) { + foreach (Block ch in cce.NonNull((GotoCmd)b.TransferCmd).labelTargets) { + Contract.Assert(ch != null); + CloneBlock(ch); + } + } + foreach (Block p in b.Predecessors) { + Contract.Assert(p != null); + res.Predecessors.Add(CloneBlock(p)); + } + return res; + } + + // this one copies backwards + Block CopyBlock(Block b) { + Contract.Requires(b != null); + Contract.Ensures(Contract.Result<Block>() != null); + + Block fake_res; + if (copies.TryGetValue(b, out fake_res)) { + // fake_res should be Block! but the compiler fails + return cce.NonNull(fake_res); + } + Block res; + List<Cmd> seq = new List<Cmd>(); + foreach (Cmd c in b.Cmds) { + Contract.Assert(c != null); + AssertCmd turn = c as AssertCmd; + if (!turnAssertIntoAssumes || turn == null) { + seq.Add(c); + } else { + seq.Add(AssertTurnedIntoAssume(turn)); + } + } + res = new Block(b.tok, b.Label, seq, null); + copies[b] = res; + foreach (Block p in b.Predecessors) { + Contract.Assert(p != null); + res.Predecessors.Add(CopyBlock(p)); + } + return res; + } + + List<Block> GetCopiedBlocks() { + Contract.Ensures(cce.NonNullElements(Contract.Result<List<Block>>())); + + // the order of nodes in res is random (except for the first one, being the entry) + List<Block> res = new List<Block>(); + res.Add(copies[initial]); + + foreach (KeyValuePair<Block, Block> kv in copies) { + Contract.Assert(kv.Key != null&&kv.Value!=null); + GotoCmd go = kv.Key.TransferCmd as GotoCmd; + ReturnCmd ret = kv.Key.TransferCmd as ReturnCmd; + if (kv.Key != initial) { + res.Add(kv.Value); + } + if (go != null) { + GotoCmd copy = new GotoCmd(go.tok, new List<String>(), new List<Block>()); + kv.Value.TransferCmd = copy; + foreach (Block b in cce.NonNull(go.labelTargets)) { + Contract.Assert(b != null); + Block c; + if (copies.TryGetValue(b, out c)) { + copy.AddTarget(cce.NonNull(c)); + } + } + } else if (ret != null) { + kv.Value.TransferCmd = ret; + } else { + Contract.Assume(false); + throw new cce.UnreachableException(); + } + } + + copies.Clear(); + + return res; + } + + // check if e is true, false, !true, !false + // if so return true and the value of the expression in val + bool BooleanEval(Expr e, ref bool val) { + Contract.Requires(e != null); + LiteralExpr lit = e as LiteralExpr; + NAryExpr call = e as NAryExpr; + + if (lit != null && lit.isBool) { + val = lit.asBool; + return true; + } else if (call != null && + call.Fun is UnaryOperator && + ((UnaryOperator)call.Fun).Op == UnaryOperator.Opcode.Not && + BooleanEval(cce.NonNull(call.Args[0]), ref val)) { + val = !val; + return true; + } + // this is for the 0bv32 != 0bv32 generated by vcc + else if (call != null && + call.Fun is BinaryOperator && + ((BinaryOperator)call.Fun).Op == BinaryOperator.Opcode.Neq && + call.Args[0] is LiteralExpr && + cce.NonNull(call.Args[0]).Equals(call.Args[1])) { + val = false; + return true; + } + + return false; + } + + bool IsFalse(Expr e) { + Contract.Requires(e != null); + bool val = false; + return BooleanEval(e, ref val) && !val; + } + + bool CheckUnreachable(Block cur, List<Cmd> seq) + { + Contract.Requires(cur != null); + Contract.Requires(seq != null); + Contract.EnsuresOnThrow<UnexpectedProverOutputException>(true); + foreach (Cmd cmd in seq) + { + AssertCmd assrt = cmd as AssertCmd; + if (assrt != null && QKeyValue.FindBoolAttribute(assrt.Attributes, "PossiblyUnreachable")) + return false; + } + + DateTime start = DateTime.UtcNow; + if (CommandLineOptions.Clo.Trace) + { + System.Console.Write(" soundness smoke test #{0} ... ", id); + } + callback.OnProgress("smoke", id, id, 0.0); + + Token tok = new Token(); + tok.val = "soundness smoke test assertion"; + seq.Add(new AssertCmd(tok, Expr.False)); + Block copy = CopyBlock(cur); + Contract.Assert(copy != null); + copy.Cmds = seq; + List<Block> backup = impl.Blocks; + Contract.Assert(backup != null); + impl.Blocks = GetCopiedBlocks(); + copy.TransferCmd = new ReturnCmd(Token.NoToken); + if (CommandLineOptions.Clo.TraceVerify) + { + System.Console.WriteLine(); + System.Console.WriteLine(" --- smoke #{0}, before passify", id); + Emit(); + } + parent.CurrentLocalVariables = impl.LocVars; + ModelViewInfo mvInfo; + parent.PassifyImpl(impl, out mvInfo); + Dictionary<int, Absy> label2Absy; + Checker ch = parent.FindCheckerFor(CommandLineOptions.Clo.SmokeTimeout); + Contract.Assert(ch != null); + + ProverInterface.Outcome outcome = ProverInterface.Outcome.Undetermined; + try + { + lock (ch) + { + var exprGen = ch.TheoremProver.Context.ExprGen; + VCExpr controlFlowVariableExpr = CommandLineOptions.Clo.UseLabels ? null : exprGen.Integer(BigNum.ZERO); + + VCExpr vc = parent.GenerateVC(impl, controlFlowVariableExpr, out label2Absy, ch.TheoremProver.Context); + Contract.Assert(vc != null); + + if (!CommandLineOptions.Clo.UseLabels) + { + VCExpr controlFlowFunctionAppl = exprGen.ControlFlowFunctionApplication(exprGen.Integer(BigNum.ZERO), exprGen.Integer(BigNum.ZERO)); + VCExpr eqExpr = exprGen.Eq(controlFlowFunctionAppl, exprGen.Integer(BigNum.FromInt(impl.Blocks[0].UniqueId))); + vc = exprGen.Implies(eqExpr, vc); + } + + impl.Blocks = backup; + + if (CommandLineOptions.Clo.TraceVerify) + { + System.Console.WriteLine(" --- smoke #{0}, after passify", id); + Emit(); + } + + ch.BeginCheck(cce.NonNull(impl.Name + "_smoke" + id++), vc, new ErrorHandler(label2Absy, this.callback)); + } + + ch.ProverTask.Wait(); + + lock (ch) + { + outcome = ch.ReadOutcome(); + } + } + finally + { + ch.GoBackToIdle(); + } + + parent.CurrentLocalVariables = null; + + DateTime end = DateTime.UtcNow; + TimeSpan elapsed = end - start; + if (CommandLineOptions.Clo.Trace) + { + System.Console.WriteLine(" [{0} s] {1}", elapsed.TotalSeconds, + outcome == ProverInterface.Outcome.Valid ? "OOPS" : + "OK" + (outcome == ProverInterface.Outcome.Invalid ? "" : " (" + outcome + ")")); + } + + if (outcome == ProverInterface.Outcome.Valid) + { + // copy it again, so we get the version with calls, assignments and such + copy = CopyBlock(cur); + copy.Cmds = seq; + impl.Blocks = GetCopiedBlocks(); + TopologicalSortImpl(); + callback.OnUnreachableCode(impl); + impl.Blocks = backup; + return true; + } + return false; + } + + const bool turnAssertIntoAssumes = false; + + void DFS(Block cur) { + Contract.Requires(cur != null); + Contract.EnsuresOnThrow<UnexpectedProverOutputException>(true); + if (visited.Contains(cur)) + return; + visited.Add(cur); + + List<Cmd> seq = new List<Cmd>(); + foreach (Cmd cmd_ in cur.Cmds) { + Cmd cmd = cmd_; + Contract.Assert(cmd != null); + AssertCmd assrt = cmd as AssertCmd; + AssumeCmd assm = cmd as AssumeCmd; + CallCmd call = cmd as CallCmd; + + bool assumeFalse = false; + + if (assrt != null) { + // we're not going any further + // it's clear the user expected unreachable code here + // it's not clear where did he expect it, maybe it would be right to insert + // a check just one command before + if (IsFalse(assrt.Expr)) + return; + +#if TURN_ASSERT_INFO_ASSUMES + if (turnAssertIntoAssumes) { + cmd = AssertTurnedIntoAssume(assrt); + } +#endif + } else if (assm != null) { + if (IsFalse(assm.Expr)) + assumeFalse = true; + } else if (call != null) { + foreach (Ensures e in (cce.NonNull(call.Proc)).Ensures) { + Contract.Assert(e != null); + if (IsFalse(e.Condition)) + assumeFalse = true; + } + } + + if (assumeFalse) { + CheckUnreachable(cur, seq); + return; + } + + seq.Add(cmd); + } + + + GotoCmd go = cur.TransferCmd as GotoCmd; + ReturnCmd ret = cur.TransferCmd as ReturnCmd; + + Contract.Assume(!(go != null && go.labelTargets == null && go.labelNames != null && go.labelNames.Count > 0)); + + if (ret != null || (go != null && cce.NonNull(go.labelTargets).Count == 0)) { + // we end in return, so there will be no more places to check + CheckUnreachable(cur, seq); + } else if (go != null) { + bool needToCheck = true; + // if all of our children have more than one parent, then + // we're in the right place to check + foreach (Block target in cce.NonNull(go.labelTargets)) { + Contract.Assert(target != null); + if (target.Predecessors.Count == 1) { + needToCheck = false; + } + } + if (needToCheck) { + CheckUnreachable(cur, seq); + } + foreach (Block target in go.labelTargets) { + Contract.Assert(target != null); + DFS(target); + } + } + } + + class ErrorHandler : ProverInterface.ErrorHandler { + Dictionary<int, Absy> label2Absy; + VerifierCallback callback; + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(label2Absy != null); + Contract.Invariant(callback != null); + } + + + public ErrorHandler(Dictionary<int, Absy> label2Absy, VerifierCallback callback) { + Contract.Requires(label2Absy != null); + Contract.Requires(callback != null); + this.label2Absy = label2Absy; + this.callback = callback; + } + + public override Absy Label2Absy(string label) { + //Contract.Requires(label != null); + Contract.Ensures(Contract.Result<Absy>() != null); + + int id = int.Parse(label); + return cce.NonNull((Absy)label2Absy[id]); + } + + public override void OnProverWarning(string msg) { + //Contract.Requires(msg != null); + this.callback.OnWarning(msg); + } + } + } + + + #endregion + + #region Splitter + class Split { + class BlockStats { + public bool big_block; + public int id; + public double assertion_cost; + public double assumption_cost; // before multiplier + public double incomming_paths; + public List<Block>/*!>!*/ virtual_successors = new List<Block>(); + public List<Block>/*!>!*/ virtual_predecesors = new List<Block>(); + public HashSet<Block> reachable_blocks; + public readonly Block block; + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(cce.NonNullElements(virtual_successors)); + Contract.Invariant(cce.NonNullElements(virtual_predecesors)); + Contract.Invariant(block != null); + } + + + public BlockStats(Block b, int i) { + Contract.Requires(b != null); + block = b; + assertion_cost = -1; + id = i; + } + } + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(cce.NonNullElements(blocks)); + Contract.Invariant(cce.NonNullElements(big_blocks)); + Contract.Invariant(cce.NonNullDictionaryAndValues(stats)); + Contract.Invariant(cce.NonNullElements(assumized_branches)); + Contract.Invariant(gotoCmdOrigins != null); + Contract.Invariant(parent != null); + Contract.Invariant(impl != null); + Contract.Invariant(copies != null); + Contract.Invariant(cce.NonNull(protected_from_assert_to_assume)); + Contract.Invariant(cce.NonNull(keep_at_all)); + } + + + readonly List<Block> blocks; + readonly List<Block> big_blocks = new List<Block>(); + readonly Dictionary<Block/*!*/, BlockStats/*!*/>/*!*/ stats = new Dictionary<Block/*!*/, BlockStats/*!*/>(); + readonly int id; + static int current_id = -1; + Block split_block; + bool assert_to_assume; + List<Block/*!*/>/*!*/ assumized_branches = new List<Block/*!*/>(); + + double score; + bool score_computed; + double total_cost; + int assertion_count; + double assertion_cost; // without multiplication by paths + Dictionary<TransferCmd, ReturnCmd>/*!*/ gotoCmdOrigins; + readonly public VCGen/*!*/ parent; + Implementation/*!*/ impl; + + Dictionary<Block/*!*/, Block/*!*/>/*!*/ copies = new Dictionary<Block/*!*/, Block/*!*/>(); + bool doing_slice; + double slice_initial_limit; + double slice_limit; + bool slice_pos; + HashSet<Block/*!*/>/*!*/ protected_from_assert_to_assume = new HashSet<Block/*!*/>(); + HashSet<Block/*!*/>/*!*/ keep_at_all = new HashSet<Block/*!*/>(); + + // async interface + private Checker checker; + private int splitNo; + internal ErrorReporter reporter; + + public Split(List<Block/*!*/>/*!*/ blocks, Dictionary<TransferCmd, ReturnCmd>/*!*/ gotoCmdOrigins, VCGen/*!*/ par, Implementation/*!*/ impl) { + Contract.Requires(cce.NonNullElements(blocks)); + Contract.Requires(gotoCmdOrigins != null); + Contract.Requires(par != null); + Contract.Requires(impl != null); + this.blocks = blocks; + this.gotoCmdOrigins = gotoCmdOrigins; + this.parent = par; + this.impl = impl; + this.id = Interlocked.Increment(ref current_id); + } + + public double Cost { + get { + ComputeBestSplit(); + return total_cost; + } + } + + public bool LastChance { + get { + ComputeBestSplit(); + return assertion_count == 1 && score < 0; + } + } + + public string Stats { + get { + ComputeBestSplit(); + return string.Format("(cost:{0:0}/{1:0}{2})", total_cost, assertion_cost, LastChance ? " last" : ""); + } + } + + public void DumpDot(int no) { + using (System.IO.StreamWriter sw = System.IO.File.CreateText(string.Format("split.{0}.dot", no))) { + sw.WriteLine("digraph G {"); + + ComputeBestSplit(); + List<Block> saved = assumized_branches; + Contract.Assert(saved != null); + assumized_branches = new List<Block>(); + DoComputeScore(false); + assumized_branches = saved; + + foreach (Block b in big_blocks) { + Contract.Assert(b != null); + BlockStats s = GetBlockStats(b); + foreach (Block t in s.virtual_successors) { + Contract.Assert(t != null); + sw.WriteLine("n{0} -> n{1};", s.id, GetBlockStats(t).id); + } + sw.WriteLine("n{0} [label=\"{1}:\\n({2:0.0}+{3:0.0})*{4:0.0}\"{5}];", + s.id, b.Label, + s.assertion_cost, s.assumption_cost, s.incomming_paths, + s.assertion_cost > 0 ? ",shape=box" : ""); + + } + sw.WriteLine("}"); + sw.Close(); + } + + string filename = string.Format("split.{0}.bpl", no); + using (System.IO.StreamWriter sw = System.IO.File.CreateText(filename)) { + int oldPrintUnstructured = CommandLineOptions.Clo.PrintUnstructured; + CommandLineOptions.Clo.PrintUnstructured = 2; // print only the unstructured program + bool oldPrintDesugaringSetting = CommandLineOptions.Clo.PrintDesugarings; + CommandLineOptions.Clo.PrintDesugarings = false; + List<Block> backup = impl.Blocks; + Contract.Assert(backup != null); + impl.Blocks = blocks; + impl.Emit(new TokenTextWriter(filename, sw, /*setTokens=*/ false, /*pretty=*/ false), 0); + impl.Blocks = backup; + CommandLineOptions.Clo.PrintDesugarings = oldPrintDesugaringSetting; + CommandLineOptions.Clo.PrintUnstructured = oldPrintUnstructured; + } + } + + int bsid; + BlockStats GetBlockStats(Block b) { + Contract.Requires(b != null); + Contract.Ensures(Contract.Result<BlockStats>() != null); + + BlockStats s; + if (!stats.TryGetValue(b, out s)) { + s = new BlockStats(b, bsid++); + stats[b] = s; + } + return cce.NonNull(s); + } + + double AssertionCost(PredicateCmd c) { + return 1.0; + } + + void CountAssertions(Block b) { + Contract.Requires(b != null); + BlockStats s = GetBlockStats(b); + if (s.assertion_cost >= 0) + return; // already done + s.big_block = true; + s.assertion_cost = 0; + s.assumption_cost = 0; + foreach (Cmd c in b.Cmds) { + if (c is AssertCmd) { + double cost = AssertionCost((AssertCmd)c); + s.assertion_cost += cost; + assertion_count++; + assertion_cost += cost; + } else if (c is AssumeCmd) { + s.assumption_cost += AssertionCost((AssumeCmd)c); + } + } + foreach (Block c in Exits(b)) { + Contract.Assert(c != null); + s.virtual_successors.Add(c); + } + if (s.virtual_successors.Count == 1) { + Block next = s.virtual_successors[0]; + BlockStats se = GetBlockStats(next); + CountAssertions(next); + if (next.Predecessors.Count > 1 || se.virtual_successors.Count != 1) + return; + s.virtual_successors[0] = se.virtual_successors[0]; + s.assertion_cost += se.assertion_cost; + s.assumption_cost += se.assumption_cost; + se.big_block = false; + } + } + + HashSet<Block/*!*/>/*!*/ ComputeReachableNodes(Block/*!*/ b) { + Contract.Requires(b != null); + Contract.Ensures(cce.NonNull(Contract.Result<HashSet<Block/*!*/>>())); + BlockStats s = GetBlockStats(b); + if (s.reachable_blocks != null) { + return s.reachable_blocks; + } + HashSet<Block/*!*/> blocks = new HashSet<Block/*!*/>(); + s.reachable_blocks = blocks; + blocks.Add(b); + foreach (Block/*!*/ succ in Exits(b)) { + Contract.Assert(succ != null); + foreach (Block r in ComputeReachableNodes(succ)) { + Contract.Assert(r != null); + blocks.Add(r); + } + } + return blocks; + } + + double ProverCost(double vc_cost) { + return vc_cost * vc_cost; + } + + void ComputeBestSplit() { + if (score_computed) + return; + score_computed = true; + + assertion_count = 0; + + foreach (Block b in blocks) { + Contract.Assert(b != null); + CountAssertions(b); + } + + foreach (Block b in blocks) { + Contract.Assert(b != null); + BlockStats bs = GetBlockStats(b); + if (bs.big_block) { + big_blocks.Add(b); + foreach (Block ch in bs.virtual_successors) { + Contract.Assert(ch != null); + BlockStats chs = GetBlockStats(ch); + if (!chs.big_block) { + Console.WriteLine("non-big {0} accessed from {1}", ch, b); + DumpDot(-1); + Contract.Assert(false); + throw new cce.UnreachableException(); + } + chs.virtual_predecesors.Add(b); + } + } + } + + assumized_branches.Clear(); + total_cost = ProverCost(DoComputeScore(false)); + + score = double.PositiveInfinity; + Block best_split = null; + List<Block> saved_branches = new List<Block>(); + + foreach (Block b in big_blocks) { + Contract.Assert(b != null); + GotoCmd gt = b.TransferCmd as GotoCmd; + if (gt == null) + continue; + List<Block> targ = cce.NonNull(gt.labelTargets); + if (targ.Count < 2) + continue; + // caution, we only consider two first exits + + double left0, right0, left1, right1; + split_block = b; + + assumized_branches.Clear(); + assumized_branches.Add(cce.NonNull(targ[0])); + left0 = DoComputeScore(true); + right0 = DoComputeScore(false); + + assumized_branches.Clear(); + for (int idx = 1; idx < targ.Count; idx++) { + assumized_branches.Add(cce.NonNull(targ[idx])); + } + left1 = DoComputeScore(true); + right1 = DoComputeScore(false); + + double current_score = ProverCost(left1) + ProverCost(right1); + double other_score = ProverCost(left0) + ProverCost(right0); + + if (other_score < current_score) { + current_score = other_score; + assumized_branches.Clear(); + assumized_branches.Add(cce.NonNull(targ[0])); + } + + if (current_score < score) { + score = current_score; + best_split = split_block; + saved_branches.Clear(); + saved_branches.AddRange(assumized_branches); + } + } + + if (CommandLineOptions.Clo.VcsPathSplitMult * score > total_cost) { + split_block = null; + score = -1; + } else { + assumized_branches = saved_branches; + split_block = best_split; + } + } + + void UpdateIncommingPaths(BlockStats s) { + Contract.Requires(s != null); + if (s.incomming_paths < 0.0) { + int count = 0; + s.incomming_paths = 0.0; + if (!keep_at_all.Contains(s.block)) + return; + foreach (Block b in s.virtual_predecesors) { + Contract.Assert(b != null); + BlockStats ch = GetBlockStats(b); + Contract.Assert(ch != null); + UpdateIncommingPaths(ch); + if (ch.incomming_paths > 0.0) { + s.incomming_paths += ch.incomming_paths; + count++; + } + } + if (count > 1) { + s.incomming_paths *= CommandLineOptions.Clo.VcsPathJoinMult; + } + } + } + + void ComputeBlockSetsHelper(Block b, bool allow_small) { + Contract.Requires(b != null); + if (keep_at_all.Contains(b)) + return; + keep_at_all.Add(b); + + if (allow_small) { + foreach (Block ch in Exits(b)) { + Contract.Assert(ch != null); + if (b == split_block && assumized_branches.Contains(ch)) + continue; + ComputeBlockSetsHelper(ch, allow_small); + } + } else { + foreach (Block ch in GetBlockStats(b).virtual_successors) { + Contract.Assert(ch != null); + if (b == split_block && assumized_branches.Contains(ch)) + continue; + ComputeBlockSetsHelper(ch, allow_small); + } + } + } + + void ComputeBlockSets(bool allow_small) { + protected_from_assert_to_assume.Clear(); + keep_at_all.Clear(); + + Debug.Assert(split_block == null || GetBlockStats(split_block).big_block); + Debug.Assert(GetBlockStats(blocks[0]).big_block); + + if (assert_to_assume) { + foreach (Block b in allow_small ? blocks : big_blocks) { + Contract.Assert(b != null); + if (ComputeReachableNodes(b).Contains(cce.NonNull(split_block))) { + keep_at_all.Add(b); + } + } + + foreach (Block b in assumized_branches) { + Contract.Assert(b != null); + foreach (Block r in ComputeReachableNodes(b)) { + Contract.Assert(r != null); + if (allow_small || GetBlockStats(r).big_block) { + keep_at_all.Add(r); + protected_from_assert_to_assume.Add(r); + } + } + } + } else { + ComputeBlockSetsHelper(blocks[0], allow_small); + } + } + + bool ShouldAssumize(Block b) { + Contract.Requires(b != null); + return assert_to_assume && !protected_from_assert_to_assume.Contains(b); + } + + double DoComputeScore(bool aa) { + assert_to_assume = aa; + ComputeBlockSets(false); + + foreach (Block b in big_blocks) { + Contract.Assert(b != null); + GetBlockStats(b).incomming_paths = -1.0; + } + + GetBlockStats(blocks[0]).incomming_paths = 1.0; + + double cost = 0.0; + foreach (Block b in big_blocks) { + Contract.Assert(b != null); + if (keep_at_all.Contains(b)) { + BlockStats s = GetBlockStats(b); + UpdateIncommingPaths(s); + double local = s.assertion_cost; + if (ShouldAssumize(b)) { + local = (s.assertion_cost + s.assumption_cost) * CommandLineOptions.Clo.VcsAssumeMult; + } else { + local = s.assumption_cost * CommandLineOptions.Clo.VcsAssumeMult + s.assertion_cost; + } + local = local + local * s.incomming_paths * CommandLineOptions.Clo.VcsPathCostMult; + cost += local; + } + } + + return cost; + } + + 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 (!doing_slice && !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 the_new = c; + bool swap = false; + if (a != null) { + if (doing_slice) { + double cost = AssertionCost(a); + bool first = (slice_limit - cost) >= 0 || slice_initial_limit == slice_limit; + slice_limit -= cost; + swap = slice_pos == first; + } else if (assert_to_assume) { + swap = true; + } else { + Contract.Assert(false); + throw new cce.UnreachableException(); + } + + if (swap) { + the_new = AssertTurnedIntoAssume(a); + } + } + res.Add(the_new); + } + return res; + } + + Block CloneBlock(Block b) { + Contract.Requires(b != null); + Contract.Ensures(Contract.Result<Block>() != null); + + Block res; + if (copies.TryGetValue(b, out res)) { + return cce.NonNull(res); + } + res = new Block(b.tok, b.Label, SliceCmds(b), b.TransferCmd); + GotoCmd gt = b.TransferCmd as GotoCmd; + copies[b] = res; + if (gt != null) { + GotoCmd newGoto = new GotoCmd(gt.tok, new List<String>(), new List<Block>()); + res.TransferCmd = newGoto; + int pos = 0; + foreach (Block ch in cce.NonNull(gt.labelTargets)) { + Contract.Assert(ch != null); + Contract.Assert(doing_slice || + (assert_to_assume || (keep_at_all.Contains(ch) || assumized_branches.Contains(ch)))); + if (doing_slice || + ((b != split_block || assumized_branches.Contains(ch) == assert_to_assume) && + keep_at_all.Contains(ch))) { + newGoto.AddTarget(CloneBlock(ch)); + } + pos++; + } + } + return res; + } + + Split DoSplit() { + Contract.Ensures(Contract.Result<Split>() != null); + + copies.Clear(); + CloneBlock(blocks[0]); + List<Block> newBlocks = new List<Block>(); + Dictionary<TransferCmd, ReturnCmd> newGotoCmdOrigins = new Dictionary<TransferCmd, ReturnCmd>(); + foreach (Block b in blocks) { + Contract.Assert(b != null); + Block tmp; + if (copies.TryGetValue(b, out tmp)) { + newBlocks.Add(cce.NonNull(tmp)); + if (gotoCmdOrigins.ContainsKey(b.TransferCmd)) { + newGotoCmdOrigins[tmp.TransferCmd] = gotoCmdOrigins[b.TransferCmd]; + } + + foreach (Block p in b.Predecessors) { + Contract.Assert(p != null); + Block tmp2; + if (copies.TryGetValue(p, out tmp2)) { + tmp.Predecessors.Add(tmp2); + } + } + } + } + + return new Split(newBlocks, newGotoCmdOrigins, parent, impl); + } + + Split SplitAt(int idx) { + Contract.Ensures(Contract.Result<Split>() != null); + + assert_to_assume = idx == 0; + doing_slice = false; + ComputeBlockSets(true); + + return DoSplit(); + } + + Split SliceAsserts(double limit, bool pos) { + Contract.Ensures(Contract.Result<Split>() != null); + + slice_pos = pos; + slice_limit = limit; + slice_initial_limit = limit; + doing_slice = true; + Split r = DoSplit(); + /* + Console.WriteLine("split {0} / {1} -->", limit, pos); + List<Block!> tmp = impl.Blocks; + impl.Blocks = r.blocks; + EmitImpl(impl, false); + impl.Blocks = tmp; + */ + + return r; + } + + void Print() { + List<Block> tmp = impl.Blocks; + Contract.Assert(tmp != null); + impl.Blocks = blocks; + EmitImpl(impl, false); + impl.Blocks = tmp; + } + + public Counterexample ToCounterexample(ProverContext context) { + Contract.Requires(context != null); + Contract.Ensures(Contract.Result<Counterexample>() != null); + + List<Block> trace = new List<Block>(); + foreach (Block b in blocks) { + Contract.Assert(b != null); + trace.Add(b); + } + foreach (Block b in blocks) { + Contract.Assert(b != null); + foreach (Cmd c in b.Cmds) { + Contract.Assert(c != null); + if (c is AssertCmd) { + return AssertCmdToCounterexample((AssertCmd)c, cce.NonNull(b.TransferCmd), trace, null, null, context); + } + } + } + Contract.Assume(false); + throw new cce.UnreachableException(); + } + + /// <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="begin_split_id">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 begin_split_id, 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 && begin_split_id == -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 split_here_count = 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 begin_split_id we were given and the next split + if (split_here_count == begin_split_id) { + verify = true; + } else if (split_here_count == begin_split_id + 1) { + verify = false; + } + } else { // We're in an endPoint so we stop verifying as soon as we hit a "split_here" + verify = false; + } + split_here_count++; + } + + var asrt = c as AssertCmd; + if (verify || asrt == null) + newCmds.Add(c); + else + newCmds.Add(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 List<Split/*!*/> FindManualSplits(Implementation/*!*/ impl, Dictionary<TransferCmd, ReturnCmd>/*!*/ gotoCmdOrigins, VCGen/*!*/ par) { + Contract.Requires(impl != null); + Contract.Ensures(Contract.Result<List<Split>>() == null || cce.NonNullElements(Contract.Result<List<Split>>())); + + var splitPoints = new Dictionary<Block,int>(); + foreach (var b in impl.Blocks) { + foreach (Cmd c in b.Cmds) { + var p = c as PredicateCmd; + if (p != null && QKeyValue.FindBoolAttribute(p.Attributes, "split_here")) { + int count; + splitPoints.TryGetValue(b, out count); + splitPoints[b] = count + 1; + } + } + } + + if (splitPoints.Count() == 0) { // No manual split points here + return null; + } + + List<Split> splits = new List<Split>(); + Block entryPoint = impl.Blocks[0]; + var newEntryBlocks = DoManualSplit(impl.Blocks, entryPoint, -1, splitPoints.Keys.Contains(entryPoint), splitPoints.Keys); + splits.Add(new Split(newEntryBlocks, gotoCmdOrigins, par, impl)); // REVIEW: Does gotoCmdOrigins need to be changed at all? + + foreach (KeyValuePair<Block,int> pair in splitPoints) { + for (int i = 0; i < pair.Value; i++) { + bool blockInternalSplit = i < pair.Value - 1; // There's at least one more split, after this one, in the current block + var newBlocks = DoManualSplit(impl.Blocks, pair.Key, i, blockInternalSplit, splitPoints.Keys); + Split s = new Split(newBlocks, gotoCmdOrigins, par, impl); // REVIEW: Does gotoCmdOrigins need to be changed at all? + splits.Add(s); + } + } + + return splits; + } + + public static List<Split/*!*/>/*!*/ DoSplit(Split initial, double max_cost, int max) { + Contract.Requires(initial != null); + Contract.Ensures(cce.NonNullElements(Contract.Result<List<Split>>())); + + List<Split> res = new List<Split>(); + res.Add(initial); + + while (res.Count < max) { + Split best = null; + int best_idx = 0, pos = 0; + foreach (Split s in res) { + Contract.Assert(s != null); + s.ComputeBestSplit(); // TODO check total_cost first + if (s.total_cost > max_cost && + (best == null || best.total_cost < s.total_cost) && + (s.assertion_count > 1 || s.split_block != null)) { + best = s; + best_idx = pos; + } + pos++; + } + + if (best == null) + break; // no split found + + Split s0, s1; + + bool split_stats = CommandLineOptions.Clo.TraceVerify; + + if (split_stats) { + Console.WriteLine("{0} {1} -->", best.split_block == null ? "SLICE" : ("SPLIT@" + best.split_block.Label), best.Stats); + if (best.split_block != null) { + GotoCmd g = best.split_block.TransferCmd as GotoCmd; + if (g != null) { + Console.Write(" exits: "); + foreach (Block b in cce.NonNull(g.labelTargets)) { + Contract.Assert(b != null); + Console.Write("{0} ", b.Label); + } + Console.WriteLine(""); + Console.Write(" assumized: "); + foreach (Block b in best.assumized_branches) { + Contract.Assert(b != null); + Console.Write("{0} ", b.Label); + } + Console.WriteLine(""); + } + } + } + + if (best.split_block != null) { + s0 = best.SplitAt(0); + s1 = best.SplitAt(1); + } else { + best.split_block = null; + s0 = best.SliceAsserts(best.assertion_cost / 2, true); + s1 = best.SliceAsserts(best.assertion_cost / 2, false); + } + + if (true) { + List<Block> ss = new List<Block>(); + ss.Add(s0.blocks[0]); + ss.Add(s1.blocks[0]); + try { + best.SoundnessCheck(new HashSet<List<Block>>(new BlockListComparer()), best.blocks[0], ss); + } catch (System.Exception e) { + Console.WriteLine(e); + best.DumpDot(-1); + s0.DumpDot(-2); + s1.DumpDot(-3); + Contract.Assert(false); + throw new cce.UnreachableException(); + } + } + + if (split_stats) { + s0.ComputeBestSplit(); + s1.ComputeBestSplit(); + Console.WriteLine(" --> {0}", s0.Stats); + Console.WriteLine(" --> {0}", s1.Stats); + } + + if (CommandLineOptions.Clo.TraceVerify) { + best.Print(); + } + + res[best_idx] = s0; + res.Add(s1); + } + + return res; + } + + class BlockListComparer : IEqualityComparer<List<Block>> + { + public bool Equals(List<Block> x, List<Block> y) + { + return x == y || x.SequenceEqual(y); + } + + public int GetHashCode(List<Block> obj) + { + int h = 0; + Contract.Assume(obj != null); + foreach (var b in obj) + { + if (b != null) + { + h += b.GetHashCode(); + } + } + return h; + } + } + + public Checker Checker { + get { + Contract.Ensures(Contract.Result<Checker>() != null); + + Contract.Assert(checker != null); + return checker; + } + } + + public Task ProverTask { + get { + Contract.Assert(checker != null); + return checker.ProverTask; + } + } + + public void ReadOutcome(ref Outcome cur_outcome, out bool prover_failed) { + Contract.EnsuresOnThrow<UnexpectedProverOutputException>(true); + ProverInterface.Outcome outcome = cce.NonNull(checker).ReadOutcome(); + + if (CommandLineOptions.Clo.Trace && splitNo >= 0) { + System.Console.WriteLine(" --> split #{0} done, [{1} s] {2}", splitNo, checker.ProverRunTime.TotalSeconds, outcome); + } + + if (CommandLineOptions.Clo.VcsDumpSplits) { + DumpDot(splitNo); + } + + prover_failed = false; + + switch (outcome) { + case ProverInterface.Outcome.Valid: + return; + case ProverInterface.Outcome.Invalid: + cur_outcome = Outcome.Errors; + return; + case ProverInterface.Outcome.OutOfMemory: + prover_failed = true; + if (cur_outcome != Outcome.Errors && cur_outcome != Outcome.Inconclusive) + cur_outcome = Outcome.OutOfMemory; + return; + case ProverInterface.Outcome.TimeOut: + prover_failed = true; + if (cur_outcome != Outcome.Errors && cur_outcome != Outcome.Inconclusive) + cur_outcome = Outcome.TimedOut; + return; + case ProverInterface.Outcome.Undetermined: + if (cur_outcome != Outcome.Errors) + cur_outcome = Outcome.Inconclusive; + return; + default: + Contract.Assert(false); + throw new cce.UnreachableException(); + } + } + + /// <summary> + /// As a side effect, updates "this.parent.CumulativeAssertionCount". + /// </summary> + public void BeginCheck(Checker checker, VerifierCallback callback, ModelViewInfo mvInfo, int no, int timeout) + { + Contract.Requires(checker != null); + Contract.Requires(callback != null); + + splitNo = no; + + impl.Blocks = blocks; + + this.checker = checker; + + Dictionary<int, Absy> label2absy = new Dictionary<int, Absy>(); + + ProverContext ctx = checker.TheoremProver.Context; + Boogie2VCExprTranslator bet = ctx.BoogieExprTranslator; + CodeExprConversionClosure cc = new CodeExprConversionClosure(label2absy, ctx); + bet.SetCodeExprConverter(cc.CodeExprToVerificationCondition); + + var exprGen = ctx.ExprGen; + VCExpr controlFlowVariableExpr = CommandLineOptions.Clo.UseLabels ? null : exprGen.Integer(BigNum.ZERO); + + VCExpr vc = parent.GenerateVCAux(impl, controlFlowVariableExpr, label2absy, checker.TheoremProver.Context); + Contract.Assert(vc != null); + + if (!CommandLineOptions.Clo.UseLabels) + { + VCExpr controlFlowFunctionAppl = exprGen.ControlFlowFunctionApplication(exprGen.Integer(BigNum.ZERO), exprGen.Integer(BigNum.ZERO)); + VCExpr eqExpr = exprGen.Eq(controlFlowFunctionAppl, exprGen.Integer(BigNum.FromInt(impl.Blocks[0].UniqueId))); + vc = exprGen.Implies(eqExpr, vc); + } + + if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Local) + { + reporter = new ErrorReporterLocal(gotoCmdOrigins, label2absy, impl.Blocks, parent.incarnationOriginMap, callback, mvInfo, cce.NonNull(this.Checker.TheoremProver.Context), parent.program); + } + else + { + reporter = new ErrorReporter(gotoCmdOrigins, label2absy, impl.Blocks, parent.incarnationOriginMap, callback, mvInfo, this.Checker.TheoremProver.Context, parent.program); + } + + if (CommandLineOptions.Clo.TraceVerify && no >= 0) + { + Console.WriteLine("-- after split #{0}", no); + Print(); + } + + string desc = cce.NonNull(impl.Name); + if (no >= 0) + desc += "_split" + no; + checker.BeginCheck(desc, vc, reporter); + } + + private void SoundnessCheck(HashSet<List<Block>/*!*/>/*!*/ cache, Block/*!*/ orig, List<Block/*!*/>/*!*/ copies) { + Contract.Requires(cce.NonNull(cache)); + Contract.Requires(orig != null); + Contract.Requires(copies != null); + { + var t = new List<Block> { orig }; + foreach (Block b in copies) { + Contract.Assert(b != null); + t.Add(b); + } + if (cache.Contains(t)) { + return; + } + cache.Add(t); + } + + for (int i = 0; i < orig.Cmds.Count; ++i) { + Cmd cmd = orig.Cmds[i]; + if (cmd is AssertCmd) { + int found = 0; + foreach (Block c in copies) { + Contract.Assert(c != null); + if (c.Cmds[i] == cmd) { + found++; + } + } + if (found == 0) { + throw new System.Exception(string.Format("missing assertion: {0}({1})", cmd.tok.filename, cmd.tok.line)); + } + } + } + + foreach (Block exit in Exits(orig)) { + Contract.Assert(exit != null); + List<Block> newcopies = new List<Block>(); + foreach (Block c in copies) { + foreach (Block cexit in Exits(c)) { + Contract.Assert(cexit != null); + if (cexit.Label == exit.Label) { + newcopies.Add(cexit); + } + } + } + if (newcopies.Count == 0) { + throw new System.Exception("missing exit " + exit.Label); + } + SoundnessCheck(cache, exit, newcopies); + } + } + } + #endregion + + + public class CodeExprConversionClosure + { + Dictionary<int, Absy> label2absy; + ProverContext ctx; + public CodeExprConversionClosure(Dictionary<int, Absy> label2absy, ProverContext ctx) + { + this.label2absy = label2absy; + this.ctx = ctx; + } + + public VCExpr CodeExprToVerificationCondition(CodeExpr codeExpr, Hashtable blockVariables, List<VCExprLetBinding> bindings, bool isPositiveContext) + { + VCGen vcgen = new VCGen(new Program(), null, false, new List<Checker>()); + vcgen.variable2SequenceNumber = new Dictionary<Variable, int>(); + vcgen.incarnationOriginMap = new Dictionary<Incarnation, Absy>(); + vcgen.CurrentLocalVariables = codeExpr.LocVars; + + ResetPredecessors(codeExpr.Blocks); + vcgen.AddBlocksBetween(codeExpr.Blocks); + Dictionary<Variable, Expr> gotoCmdOrigins = vcgen.ConvertBlocks2PassiveCmd(codeExpr.Blocks, new List<IdentifierExpr>(), new ModelViewInfo(codeExpr)); + int ac; // computed, but then ignored for this CodeExpr + VCExpr startCorrect = VCGen.LetVCIterative(codeExpr.Blocks, null, label2absy, ctx, out ac, isPositiveContext); + VCExpr vce = ctx.ExprGen.Let(bindings, startCorrect); + if (vcgen.CurrentLocalVariables.Count != 0) + { + Boogie2VCExprTranslator translator = ctx.BoogieExprTranslator; + List<VCExprVar> boundVars = new List<VCExprVar>(); + foreach (Variable v in vcgen.CurrentLocalVariables) + { + Contract.Assert(v != null); + VCExprVar ev = translator.LookupVariable(v); + Contract.Assert(ev != null); + boundVars.Add(ev); + if (v.TypedIdent.Type.Equals(Bpl.Type.Bool)) + { + // add an antecedent (tickleBool ev) to help the prover find a possible trigger + vce = ctx.ExprGen.Implies(ctx.ExprGen.Function(VCExpressionGenerator.TickleBoolOp, ev), vce); + } + } + vce = ctx.ExprGen.Forall(boundVars, new List<VCTrigger>(), vce); + } + if (isPositiveContext) + { + vce = ctx.ExprGen.Not(vce); + } + return vce; + } + } + + public VCExpr GenerateVC(Implementation/*!*/ impl, VCExpr controlFlowVariableExpr, out Dictionary<int, Absy>/*!*/ label2absy, ProverContext proverContext, IList<VCExprVar> namedAssumeVars = null) + { + Contract.Requires(impl != null); + Contract.Requires(proverContext != null); + Contract.Ensures(Contract.ValueAtReturn(out label2absy) != null); + Contract.Ensures(Contract.Result<VCExpr>() != null); + + label2absy = new Dictionary<int, Absy>(); + return GenerateVCAux(impl, controlFlowVariableExpr, label2absy, proverContext, namedAssumeVars: namedAssumeVars); + } + + public VCExpr GenerateVCAux(Implementation/*!*/ impl, VCExpr controlFlowVariableExpr, Dictionary<int, Absy>/*!*/ label2absy, ProverContext proverContext, IList<VCExprVar> namedAssumeVars = null) { + Contract.Requires(impl != null); + Contract.Requires(proverContext != null); + Contract.Ensures(Contract.Result<VCExpr>() != null); + + TypecheckingContext tc = new TypecheckingContext(null); + impl.Typecheck(tc); + + VCExpr vc; + int assertionCount; + switch (CommandLineOptions.Clo.vcVariety) { + case CommandLineOptions.VCVariety.Structured: + vc = VCViaStructuredProgram(impl, label2absy, proverContext, out assertionCount); + break; + case CommandLineOptions.VCVariety.Block: + vc = FlatBlockVC(impl, label2absy, false, false, false, proverContext, out assertionCount); + break; + case CommandLineOptions.VCVariety.BlockReach: + vc = FlatBlockVC(impl, label2absy, false, true, false, proverContext, out assertionCount); + break; + case CommandLineOptions.VCVariety.Local: + vc = FlatBlockVC(impl, label2absy, true, false, false, proverContext, out assertionCount); + break; + case CommandLineOptions.VCVariety.BlockNested: + vc = NestedBlockVC(impl, label2absy, false, proverContext, out assertionCount); + break; + case CommandLineOptions.VCVariety.BlockNestedReach: + vc = NestedBlockVC(impl, label2absy, true, proverContext, out assertionCount); + break; + case CommandLineOptions.VCVariety.Dag: + if (cce.NonNull(CommandLineOptions.Clo.TheProverFactory).SupportsDags || CommandLineOptions.Clo.FixedPointEngine != null) { + vc = DagVC(cce.NonNull(impl.Blocks[0]), controlFlowVariableExpr, label2absy, new Hashtable/*<Block, VCExpr!>*/(), proverContext, out assertionCount); + } else { + vc = LetVC(cce.NonNull(impl.Blocks[0]), controlFlowVariableExpr, label2absy, proverContext, out assertionCount); + } + break; + case CommandLineOptions.VCVariety.DagIterative: + vc = LetVCIterative(impl.Blocks, controlFlowVariableExpr, label2absy, proverContext, out assertionCount); + break; + case CommandLineOptions.VCVariety.Doomed: + vc = FlatBlockVC(impl, label2absy, false, false, true, proverContext, out assertionCount); + break; + default: + Contract.Assert(false); + throw new cce.UnreachableException(); // unexpected enumeration value + } + CumulativeAssertionCount += assertionCount; + return vc; + } + + void CheckIntAttributeOnImpl(Implementation impl, string name, ref int val) { + Contract.Requires(impl != null); + Contract.Requires(name != null); + if (!(cce.NonNull(impl.Proc).CheckIntAttribute(name, ref val) || !impl.CheckIntAttribute(name, ref val))) { + Console.WriteLine("ignoring ill-formed {:{0} ...} attribute on {1}, parameter should be an int", name, impl.Name); + } + } + + public override Outcome VerifyImplementation(Implementation/*!*/ impl, VerifierCallback/*!*/ callback) { + Contract.EnsuresOnThrow<UnexpectedProverOutputException>(true); + + if (impl.SkipVerification) { + return Outcome.Inconclusive; // not sure about this one + } + + callback.OnProgress("VCgen", 0, 0, 0.0); + + Stopwatch watch = new Stopwatch(); + if (_print_time) + { + Console.WriteLine("Checking function {0}", impl.Name); + watch.Reset(); + watch.Start(); + } + + ConvertCFG2DAG(impl); + + SmokeTester smoke_tester = null; + if (CommandLineOptions.Clo.SoundnessSmokeTest) { + smoke_tester = new SmokeTester(this, impl, callback); + smoke_tester.Copy(); + } + + ModelViewInfo mvInfo; + var gotoCmdOrigins = PassifyImpl(impl, out mvInfo); + + // If "expand" attribute is supplied, expand any assertion of conjunctions into multiple assertions, one per conjunct + foreach (var b in impl.Blocks) + { + List<Cmd> newCmds = new List<Cmd>(); + bool changed = false; + foreach (var c in b.Cmds) + { + var a = c as AssertCmd; + var ar = c as AssertRequiresCmd; + var ae = c as AssertEnsuresCmd; + var ai = c as LoopInitAssertCmd; + var am = c as LoopInvMaintainedAssertCmd; + // TODO: + //use Duplicator and Substituter rather than new + //nested IToken? + //document expand attribute (search for {:ignore}, for example) + //fix up new CallCmd, new Requires, new Ensures in CivlRefinement.cs + Func<Expr,Expr,Expr> withType = (Expr from, Expr to) => + { + NAryExpr nFrom = from as NAryExpr; + NAryExpr nTo = to as NAryExpr; + to.Type = from.Type; + if (nFrom != null && nTo != null) nTo.TypeParameters = nFrom.TypeParameters; + return to; + }; + + Action<int,Expr,Action<Expr>> traverse = null; + traverse = (depth, e, act) => + { + ForallExpr forall = e as ForallExpr; + NAryExpr nary = e as NAryExpr; + if (forall != null) + { + traverse(depth, forall.Body, e1 => act(withType(forall, + new ForallExpr(e1.tok, forall.TypeParameters, forall.Dummies, forall.Attributes, forall.Triggers, e1)))); + return; + } + if (nary != null) + { + var args = nary.Args; + IAppliable fun = nary.Fun; + BinaryOperator bop = fun as BinaryOperator; + FunctionCall call = fun as FunctionCall; + if (bop != null) + { + switch (bop.Op) + { + case BinaryOperator.Opcode.And: + traverse(depth, args[0], act); + traverse(depth, args[1], act); + return; + case BinaryOperator.Opcode.Imp: + traverse(depth, args[1], e1 => act(withType(nary, + new NAryExpr(e1.tok, fun, new List<Expr>() { args[0], e1 })))); + return; + } + } + if (depth > 0 && call != null && call.Func != null) + { + Function cf = call.Func; + Expr body = cf.Body; + List<Variable> ins = cf.InParams; + if (body == null && cf.DefinitionAxiom != null) + { + ForallExpr all = cf.DefinitionAxiom.Expr as ForallExpr; + if (all != null) + { + NAryExpr def = all.Body as NAryExpr; + if (def != null && def.Fun is BinaryOperator && ((BinaryOperator) (def.Fun)).Op == BinaryOperator.Opcode.Iff) + { + body = def.Args[1]; + ins = all.Dummies; + } + } + } + if (body != null) + { + Func<Expr,Expr> new_f = e1 => + { + Function f = new Function(cf.tok, "expand<" + cf.Name + ">", cf.TypeParameters, ins, cf.OutParams[0], cf.Comment); + f.Body = e1; + Token tok = new Token(e1.tok.line, e1.tok.col); + tok.filename = e.tok.filename + "(" + e.tok.line + "," + e.tok.col + ") --> " + e1.tok.filename; + return withType(nary, new NAryExpr(tok, new FunctionCall(f), args)); + }; + traverse(depth - 1, body, e1 => act(new_f(e1))); + return; + } + } + } + act(e); + }; + + if (a != null) + { + var attr = a.Attributes; + if (ar != null && ar.Requires.Attributes != null) attr = ar.Requires.Attributes; + if (ar != null && ar.Call.Attributes != null) attr = ar.Call.Attributes; + if (ae != null && ae.Ensures.Attributes != null) attr = ae.Ensures.Attributes; + if (QKeyValue.FindExprAttribute(attr, "expand") != null || QKeyValue.FindBoolAttribute(attr, "expand")) + { + int depth = QKeyValue.FindIntAttribute(attr, "expand", 100); + Func<Expr,Expr> fe = e => Expr.Or(a.Expr, e); + //traverse(depth, a.Expr, e => System.Console.WriteLine(e.GetType() + " :: " + e + " @ " + e.tok.line + ", " + e.tok.col)); + traverse(depth, a.Expr, e => + { + AssertCmd new_c = + (ar != null) ? new AssertRequiresCmd(ar.Call, new Requires(e.tok, ar.Requires.Free, fe(e), ar.Requires.Comment)) : + (ae != null) ? new AssertEnsuresCmd(new Ensures(e.tok, ae.Ensures.Free, fe(e), ae.Ensures.Comment)) : + (ai != null) ? new LoopInitAssertCmd(e.tok, fe(e)) : + (am != null) ? new LoopInvMaintainedAssertCmd(e.tok, fe(e)) : + new AssertCmd(e.tok, fe(e)); + new_c.Attributes = new QKeyValue(e.tok, "subsumption", new List<object>() { new LiteralExpr(e.tok, BigNum.FromInt(0)) }, a.Attributes); + newCmds.Add(new_c); + }); + } + newCmds.Add(c); + changed = true; + } + else + { + newCmds.Add(c); + } + } + if (changed) b.Cmds = newCmds; + } + + double max_vc_cost = CommandLineOptions.Clo.VcsMaxCost; + int tmp_max_vc_cost = -1, max_splits = CommandLineOptions.Clo.VcsMaxSplits, + max_kg_splits = CommandLineOptions.Clo.VcsMaxKeepGoingSplits; + CheckIntAttributeOnImpl(impl, "vcs_max_cost", ref tmp_max_vc_cost); + CheckIntAttributeOnImpl(impl, "vcs_max_splits", ref max_splits); + CheckIntAttributeOnImpl(impl, "vcs_max_keep_going_splits", ref max_kg_splits); + if (tmp_max_vc_cost >= 0) { + max_vc_cost = tmp_max_vc_cost; + } + + Outcome outcome = Outcome.Correct; + + // Report all recycled failing assertions for this implementation. + if (impl.RecycledFailingAssertions != null && impl.RecycledFailingAssertions.Any()) + { + outcome = Outcome.Errors; + foreach (var a in impl.RecycledFailingAssertions) + { + var checksum = a.Checksum; + var oldCex = impl.ErrorChecksumToCachedError[checksum] as Counterexample; + if (oldCex != null) { + if (CommandLineOptions.Clo.VerifySnapshots < 3) { + callback.OnCounterexample(oldCex, null); + } else { + // If possible, we use the old counterexample, but with the location information of "a" + var cex = AssertCmdToCloneCounterexample(a, oldCex); + callback.OnCounterexample(cex, null); + // OnCounterexample may have had side effects on the RequestId and OriginalRequestId fields. We make + // any such updates available in oldCex. (Is this really a good design? --KRML) + oldCex.RequestId = cex.RequestId; + oldCex.OriginalRequestId = cex.OriginalRequestId; + } + } + } + } + + Cores = CommandLineOptions.Clo.VcsCores; + Stack<Split> work = new Stack<Split>(); + List<Split> currently_running = new List<Split>(); + ResetPredecessors(impl.Blocks); + List<Split> manual_splits = Split.FindManualSplits(impl, gotoCmdOrigins, this); + if (manual_splits != null) { + foreach (var split in manual_splits) { + work.Push(split); + } + } else { + work.Push(new Split(impl.Blocks, gotoCmdOrigins, this, impl)); + } + + bool keep_going = max_kg_splits > 1; + int total = 0; + int no = max_splits == 1 && !keep_going ? -1 : 0; + bool first_round = true; + bool do_splitting = keep_going || max_splits > 1; + double remaining_cost = 0.0, proven_cost = 0.0; + + if (do_splitting) { + remaining_cost = work.Peek().Cost; + } + + while (work.Any() || currently_running.Any()) + { + bool prover_failed = false; + Split s = null; + var isWaiting = !work.Any(); + + if (!isWaiting) + { + s = work.Peek(); + + if (first_round && max_splits > 1) + { + prover_failed = true; + remaining_cost -= s.Cost; + } + else + { + var timeout = (keep_going && s.LastChance) ? CommandLineOptions.Clo.VcsFinalAssertTimeout : + keep_going ? CommandLineOptions.Clo.VcsKeepGoingTimeout : + impl.TimeLimit; + + var checker = s.parent.FindCheckerFor(timeout, false); + try + { + if (checker == null) + { + isWaiting = true; + goto waiting; + } + else + { + s = work.Pop(); + } + + if (CommandLineOptions.Clo.Trace && no >= 0) + { + System.Console.WriteLine(" checking split {1}/{2}, {3:0.00}%, {0} ...", + s.Stats, no + 1, total, 100 * proven_cost / (proven_cost + remaining_cost)); + } + callback.OnProgress("VCprove", no < 0 ? 0 : no, total, proven_cost / (remaining_cost + proven_cost)); + + Contract.Assert(s.parent == this); + lock (checker) + { + s.BeginCheck(checker, callback, mvInfo, no, timeout); + } + + no++; + + currently_running.Add(s); + } + catch (Exception) + { + checker.GoBackToIdle(); + throw; + } + } + } + + waiting: + if (isWaiting) + { + // Wait for one split to terminate. + var tasks = currently_running.Select(splt => splt.ProverTask).ToArray(); + + if (tasks.Any()) + { + try + { + int index = Task.WaitAny(tasks); + s = currently_running[index]; + currently_running.RemoveAt(index); + + if (do_splitting) + { + remaining_cost -= s.Cost; + } + + lock (s.Checker) + { + s.ReadOutcome(ref outcome, out prover_failed); + } + + if (do_splitting) + { + if (prover_failed) + { + // even if the prover fails, we have learned something, i.e., it is + // annoying to watch Boogie say Timeout, 0.00% a couple of times + proven_cost += s.Cost / 100; + } + else + { + proven_cost += s.Cost; + } + } + callback.OnProgress("VCprove", no < 0 ? 0 : no, total, proven_cost / (remaining_cost + proven_cost)); + + if (prover_failed && !first_round && s.LastChance) + { + string msg = "some timeout"; + if (s.reporter != null && s.reporter.resourceExceededMessage != null) + { + msg = s.reporter.resourceExceededMessage; + } + callback.OnCounterexample(s.ToCounterexample(s.Checker.TheoremProver.Context), msg); + outcome = Outcome.Errors; + break; + } + } + finally + { + s.Checker.GoBackToIdle(); + } + + Contract.Assert(prover_failed || outcome == Outcome.Correct || outcome == Outcome.Errors || outcome == Outcome.Inconclusive); + } + } + + if (prover_failed) + { + int splits = first_round && max_splits > 1 ? max_splits : max_kg_splits; + + if (splits > 1) + { + List<Split> tmp = Split.DoSplit(s, max_vc_cost, splits); + Contract.Assert(tmp != null); + max_vc_cost = 1.0; // for future + first_round = false; + //tmp.Sort(new Comparison<Split!>(Split.Compare)); + foreach (Split a in tmp) + { + Contract.Assert(a != null); + work.Push(a); + total++; + remaining_cost += a.Cost; + } + if (outcome != Outcome.Errors) + { + outcome = Outcome.Correct; + } + } + else + { + Contract.Assert(outcome != Outcome.Correct); + if (outcome == Outcome.TimedOut) + { + string msg = "some timeout"; + if (s.reporter != null && s.reporter.resourceExceededMessage != null) + { + msg = s.reporter.resourceExceededMessage; + } + callback.OnTimeout(msg); + } + else if (outcome == Outcome.OutOfMemory) + { + string msg = "out of memory"; + if (s.reporter != null && s.reporter.resourceExceededMessage != null) + { + msg = s.reporter.resourceExceededMessage; + } + callback.OnOutOfMemory(msg); + } + + break; + } + } + } + + if (outcome == Outcome.Correct && smoke_tester != null) { + smoke_tester.Test(); + } + + callback.OnProgress("done", 0, 0, 1.0); + + if (_print_time) + { + watch.Stop(); + Console.WriteLine("Total time for this method: {0}", watch.Elapsed.ToString()); + } + + return outcome; + } + + public class ErrorReporter : ProverInterface.ErrorHandler { + Dictionary<TransferCmd, ReturnCmd>/*!*/ gotoCmdOrigins; + Dictionary<int, Absy>/*!*/ label2absy; + List<Block/*!*/>/*!*/ blocks; + protected Dictionary<Incarnation, Absy/*!*/>/*!*/ incarnationOriginMap; + protected VerifierCallback/*!*/ callback; + protected ModelViewInfo MvInfo; + internal string resourceExceededMessage; + static System.IO.TextWriter modelWriter; + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(gotoCmdOrigins != null); + Contract.Invariant(label2absy != null); + Contract.Invariant(cce.NonNullElements(blocks)); + Contract.Invariant(cce.NonNullDictionaryAndValues(incarnationOriginMap)); + Contract.Invariant(callback != null); + Contract.Invariant(context != null); + Contract.Invariant(program != null); + } + + + public static TextWriter ModelWriter { + get { + Contract.Ensures(Contract.Result<TextWriter>() != null); + + if (ErrorReporter.modelWriter == null) + ErrorReporter.modelWriter = CommandLineOptions.Clo.PrintErrorModelFile == null ? Console.Out : new StreamWriter(CommandLineOptions.Clo.PrintErrorModelFile, false); + return ErrorReporter.modelWriter; + } + } + + protected ProverContext/*!*/ context; + Program/*!*/ program; + + public IEnumerable<string> NecessaryAssumes + { + get { return program.NecessaryAssumes; } + } + + public void AddNecessaryAssume(string id) + { + program.NecessaryAssumes.Add(id); + } + + public ErrorReporter(Dictionary<TransferCmd, ReturnCmd>/*!*/ gotoCmdOrigins, + Dictionary<int, Absy>/*!*/ label2absy, + List<Block/*!*/>/*!*/ blocks, + Dictionary<Incarnation, Absy/*!*/>/*!*/ incarnationOriginMap, + VerifierCallback/*!*/ callback, + ModelViewInfo mvInfo, + ProverContext/*!*/ context, + Program/*!*/ program) { + Contract.Requires(gotoCmdOrigins != null); + Contract.Requires(label2absy != null); + Contract.Requires(cce.NonNullElements(blocks)); + Contract.Requires(cce.NonNullDictionaryAndValues(incarnationOriginMap)); + Contract.Requires(callback != null); + Contract.Requires(context!=null); + Contract.Requires(program!=null); + this.gotoCmdOrigins = gotoCmdOrigins; + this.label2absy = label2absy; + this.blocks = blocks; + this.incarnationOriginMap = incarnationOriginMap; + this.callback = callback; + this.MvInfo = mvInfo; + + this.context = context; + this.program = program; + } + + public override void OnModel(IList<string/*!*/>/*!*/ labels, Model model, ProverInterface.Outcome proverOutcome) { + //Contract.Requires(cce.NonNullElements(labels)); + if (CommandLineOptions.Clo.PrintErrorModel >= 1 && model != null) { + if (VC.ConditionGeneration.errorModelList != null) + { + VC.ConditionGeneration.errorModelList.Add(model); + } + + model.Write(ErrorReporter.ModelWriter); + ErrorReporter.ModelWriter.Flush(); + } + + Hashtable traceNodes = new Hashtable(); + foreach (string s in labels) { + Contract.Assert(s != null); + Absy absy = Label2Absy(s); + Contract.Assert(absy != null); + if (traceNodes.ContainsKey(absy)) + System.Console.WriteLine("Warning: duplicate label: " + s + " read while tracing nodes"); + else + traceNodes.Add(absy, null); + } + + List<Block> trace = new List<Block>(); + Block entryBlock = cce.NonNull(this.blocks[0]); + Contract.Assert(traceNodes.Contains(entryBlock)); + trace.Add(entryBlock); + + Counterexample newCounterexample = TraceCounterexample(entryBlock, traceNodes, trace, model, MvInfo, incarnationOriginMap, context, new Dictionary<TraceLocation, CalleeCounterexampleInfo>()); + + if (newCounterexample == null) + return; + + #region Map passive program errors back to original program errors + ReturnCounterexample returnExample = newCounterexample as ReturnCounterexample; + if (returnExample != null) { + foreach (Block b in returnExample.Trace) { + Contract.Assert(b != null); + Contract.Assume(b.TransferCmd != null); + ReturnCmd cmd = gotoCmdOrigins.ContainsKey(b.TransferCmd) ? gotoCmdOrigins[b.TransferCmd] : null; + if (cmd != null) { + returnExample.FailingReturn = cmd; + break; + } + } + } + #endregion + callback.OnCounterexample(newCounterexample, null); + } + + public override Absy Label2Absy(string label) { + //Contract.Requires(label != null); + Contract.Ensures(Contract.Result<Absy>() != null); + + int id = int.Parse(label); + return cce.NonNull((Absy)label2absy[id]); + } + + public override void OnResourceExceeded(string msg, IEnumerable<Tuple<AssertCmd, TransferCmd>> assertCmds = null) { + //Contract.Requires(msg != null); + resourceExceededMessage = msg; + if (assertCmds != null) + { + foreach (var cmd in assertCmds) + { + Counterexample cex = AssertCmdToCounterexample(cmd.Item1, cmd.Item2 , new List<Block>(), null, null, context); + cex.IsAuxiliaryCexForDiagnosingTimeouts = true; + callback.OnCounterexample(cex, msg); + } + } + } + + public override void OnProverWarning(string msg) { + //Contract.Requires(msg != null); + callback.OnWarning(msg); + } + } + + public class ErrorReporterLocal : ErrorReporter { + public ErrorReporterLocal(Dictionary<TransferCmd, ReturnCmd>/*!*/ gotoCmdOrigins, + Dictionary<int, Absy>/*!*/ label2absy, + List<Block/*!*/>/*!*/ blocks, + Dictionary<Incarnation, Absy/*!*/>/*!*/ incarnationOriginMap, + VerifierCallback/*!*/ callback, + ModelViewInfo mvInfo, + ProverContext/*!*/ context, + Program/*!*/ program) + : base(gotoCmdOrigins, label2absy, blocks, incarnationOriginMap, callback, mvInfo, context, program) // here for aesthetic purposes //TODO: Maybe nix? + { + Contract.Requires(gotoCmdOrigins != null); + Contract.Requires(label2absy != null); + Contract.Requires(cce.NonNullElements(blocks)); + Contract.Requires(cce.NonNullDictionaryAndValues(incarnationOriginMap)); + Contract.Requires(callback != null); + Contract.Requires(context != null); + Contract.Requires(program != null); + } + + public override void OnModel(IList<string/*!*/>/*!*/ labels, Model model, ProverInterface.Outcome proverOutcome) { + //Contract.Requires(cce.NonNullElements(labels)); + // We ignore the error model here for enhanced error message purposes. + // It is only printed to the command line. + if (CommandLineOptions.Clo.PrintErrorModel >= 1 && model != null) { + if (CommandLineOptions.Clo.PrintErrorModelFile != null) { + model.Write(ErrorReporter.ModelWriter); + ErrorReporter.ModelWriter.Flush(); + } + } + List<Block> traceNodes = new List<Block>(); + List<AssertCmd> assertNodes = new List<AssertCmd>(); + foreach (string s in labels) { + Contract.Assert(s != null); + Absy node = Label2Absy(s); + if (node is Block) { + Block b = (Block)node; + traceNodes.Add(b); + } else { + AssertCmd a = (AssertCmd)node; + assertNodes.Add(a); + } + } + Contract.Assert(assertNodes.Count > 0); + Contract.Assert(traceNodes.Count == assertNodes.Count); + + foreach (AssertCmd a in assertNodes) { + // find the corresponding Block (assertNodes.Count is likely to be 1, or small in any case, so just do a linear search here) + foreach (Block b in traceNodes) { + if (b.Cmds.Contains(a)) { + List<Block> trace = new List<Block>(); + trace.Add(b); + Counterexample newCounterexample = AssertCmdToCounterexample(a, cce.NonNull(b.TransferCmd), trace, model, MvInfo, context); + callback.OnCounterexample(newCounterexample, null); + goto NEXT_ASSERT; + } + } + Contract.Assert(false); + throw new cce.UnreachableException(); // there was no block that contains the assert + NEXT_ASSERT: { + } + } + } + } + + private void RecordCutEdge(Dictionary<Block,List<Block>> edgesCut, Block from, Block to){ + if (edgesCut != null) + { + if (!edgesCut.ContainsKey(from)) + edgesCut.Add(from, new List<Block>()); + edgesCut[from].Add(to); + } + } + + public void ConvertCFG2DAG(Implementation impl, Dictionary<Block,List<Block>> edgesCut = null, int taskID = -1) + { + Contract.Requires(impl != null); + impl.PruneUnreachableBlocks(); // This is needed for VCVariety.BlockNested, and is otherwise just an optimization + + CurrentLocalVariables = impl.LocVars; + variable2SequenceNumber = new Dictionary<Variable, int>(); + incarnationOriginMap = new Dictionary<Incarnation, Absy>(); + + #region Debug Tracing + if (CommandLineOptions.Clo.TraceVerify) + { + Console.WriteLine("original implementation"); + EmitImpl(impl, false); + } + #endregion + + #region Debug Tracing + if (CommandLineOptions.Clo.TraceVerify) + { + Console.WriteLine("after desugaring sugared commands like procedure calls"); + EmitImpl(impl, true); + } + #endregion + + // Recompute the predecessors, but first insert a dummy start node that is sure not to be the target of any goto (because the cutting of back edges + // below assumes that the start node has no predecessor) + impl.Blocks.Insert(0, new Block(new Token(-17, -4), "0", new List<Cmd>(), new GotoCmd(Token.NoToken, new List<String> { impl.Blocks[0].Label }, new List<Block> { impl.Blocks[0] }))); + ResetPredecessors(impl.Blocks); + + var k = Math.Max(CommandLineOptions.Clo.KInductionDepth, QKeyValue.FindIntAttribute(impl.Attributes, "kInductionDepth", -1)); + if(k < 0) { + ConvertCFG2DAGStandard(impl, edgesCut, taskID); + } else { + ConvertCFG2DAGKInduction(impl, edgesCut, taskID, k); + } + + #region Debug Tracing + if (CommandLineOptions.Clo.TraceVerify) + { + Console.WriteLine("after conversion into a DAG"); + EmitImpl(impl, true); + } + #endregion + } + + private void ConvertCFG2DAGStandard(Implementation impl, Dictionary<Block, List<Block>> edgesCut, int taskID) + { + #region Convert program CFG into a DAG + + #region Use the graph library to figure out where the (natural) loops are + + #region Create the graph by adding the source node and each edge + Graph<Block> g = Program.GraphFromImpl(impl); + #endregion + + //Graph<Block> g = program.ProcessLoops(impl); + + g.ComputeLoops(); // this is the call that does all of the processing + if (!g.Reducible) + { + throw new VCGenException("Irreducible flow graphs are unsupported."); + } + + #endregion + + #region Cut the backedges, push assert/assume statements from loop header into predecessors, change them all into assume statements at top of loop, introduce havoc statements + foreach (Block header in cce.NonNull(g.Headers)) + { + Contract.Assert(header != null); + IDictionary<Block, object> backEdgeNodes = new Dictionary<Block, object>(); + foreach (Block b in cce.NonNull(g.BackEdgeNodes(header))) + { + Contract.Assert(b != null); + backEdgeNodes.Add(b, null); + } + + #region Find the (possibly empty) prefix of assert commands in the header, replace each assert with an assume of the same condition + List<Cmd> prefixOfPredicateCmdsInit = new List<Cmd>(); + List<Cmd> prefixOfPredicateCmdsMaintained = new List<Cmd>(); + for (int i = 0, n = header.Cmds.Count; i < n; i++) + { + PredicateCmd a = header.Cmds[i] as PredicateCmd; + if (a != null) + { + if (a is AssertCmd) + { + AssertCmd c = (AssertCmd)a; + AssertCmd b = null; + + if (CommandLineOptions.Clo.ConcurrentHoudini) + { + Contract.Assert(taskID >= 0); + if (CommandLineOptions.Clo.Cho[taskID].DisableLoopInvEntryAssert) + b = new LoopInitAssertCmd(c.tok, Expr.True); + else + b = new LoopInitAssertCmd(c.tok, c.Expr); + } + else + { + b = new LoopInitAssertCmd(c.tok, c.Expr); + } + + b.Attributes = c.Attributes; + b.ErrorData = c.ErrorData; + prefixOfPredicateCmdsInit.Add(b); + + if (CommandLineOptions.Clo.ConcurrentHoudini) + { + Contract.Assert(taskID >= 0); + if (CommandLineOptions.Clo.Cho[taskID].DisableLoopInvMaintainedAssert) + b = new Bpl.LoopInvMaintainedAssertCmd(c.tok, Expr.True); + else + b = new Bpl.LoopInvMaintainedAssertCmd(c.tok, c.Expr); + } + else + { + b = new Bpl.LoopInvMaintainedAssertCmd(c.tok, c.Expr); + } + + b.Attributes = c.Attributes; + b.ErrorData = c.ErrorData; + prefixOfPredicateCmdsMaintained.Add(b); + header.Cmds[i] = new AssumeCmd(c.tok, c.Expr); + } + else + { + Contract.Assert(a is AssumeCmd); + if (Bpl.CommandLineOptions.Clo.AlwaysAssumeFreeLoopInvariants) + { + // Usually, "free" stuff, like free loop invariants (and the assume statements + // that stand for such loop invariants) are ignored on the checking side. This + // command-line option changes that behavior to always assume the conditions. + prefixOfPredicateCmdsInit.Add(a); + prefixOfPredicateCmdsMaintained.Add(a); + } + } + } + else if (header.Cmds[i] is CommentCmd) + { + // ignore + } + else + { + break; // stop when an assignment statement (or any other non-predicate cmd) is encountered + } + } + #endregion + + #region Copy the prefix of predicate commands into each predecessor. Do this *before* cutting the backedge!! + for (int predIndex = 0, n = header.Predecessors.Count; predIndex < n; predIndex++) + { + Block pred = cce.NonNull(header.Predecessors[predIndex]); + + // Create a block between header and pred for the predicate commands if pred has more than one successor + GotoCmd gotocmd = cce.NonNull((GotoCmd)pred.TransferCmd); + Contract.Assert(gotocmd.labelNames != null); // if "pred" is really a predecessor, it may be a GotoCmd with at least one label + if (gotocmd.labelNames.Count > 1) + { + Block newBlock = CreateBlockBetween(predIndex, header); + impl.Blocks.Add(newBlock); + + // if pred is a back edge node, then now newBlock is the back edge node + if (backEdgeNodes.ContainsKey(pred)) + { + backEdgeNodes.Remove(pred); + backEdgeNodes.Add(newBlock, null); + } + + pred = newBlock; + } + // Add the predicate commands + if (backEdgeNodes.ContainsKey(pred)) + { + pred.Cmds.AddRange(prefixOfPredicateCmdsMaintained); + } + else + { + pred.Cmds.AddRange(prefixOfPredicateCmdsInit); + } + } + #endregion + + #region Cut the back edge + foreach (Block backEdgeNode in cce.NonNull(backEdgeNodes.Keys)) + { + Contract.Assert(backEdgeNode != null); + Debug.Assert(backEdgeNode.TransferCmd is GotoCmd, "An node was identified as the source for a backedge, but it does not have a goto command."); + GotoCmd gtc = backEdgeNode.TransferCmd as GotoCmd; + if (gtc != null && gtc.labelTargets != null && gtc.labelTargets.Count > 1) + { + // then remove the backedge by removing the target block from the list of gotos + List<Block> remainingTargets = new List<Block>(); + List<String> remainingLabels = new List<String>(); + Contract.Assume(gtc.labelNames != null); + for (int i = 0, n = gtc.labelTargets.Count; i < n; i++) + { + if (gtc.labelTargets[i] != header) + { + remainingTargets.Add(gtc.labelTargets[i]); + remainingLabels.Add(gtc.labelNames[i]); + } + else + RecordCutEdge(edgesCut, backEdgeNode, header); + } + gtc.labelTargets = remainingTargets; + gtc.labelNames = remainingLabels; + } + else + { + // This backedge is the only out-going edge from this node. + // Add an "assume false" statement to the end of the statements + // inside of the block and change the goto command to a return command. + AssumeCmd ac = new AssumeCmd(Token.NoToken, Expr.False); + backEdgeNode.Cmds.Add(ac); + backEdgeNode.TransferCmd = new ReturnCmd(Token.NoToken); + if (gtc != null && gtc.labelTargets != null && gtc.labelTargets.Count == 1) + RecordCutEdge(edgesCut, backEdgeNode, gtc.labelTargets[0]); + } + #region Remove the backedge node from the list of predecessor nodes in the header + List<Block> newPreds = new List<Block>(); + foreach (Block p in header.Predecessors) + { + if (p != backEdgeNode) + newPreds.Add(p); + } + header.Predecessors = newPreds; + #endregion + } + #endregion + + #region Collect all variables that are assigned to in all of the natural loops for which this is the header + List<Variable> varsToHavoc = VarsAssignedInLoop(g, header); + List<IdentifierExpr> havocExprs = new List<IdentifierExpr>(); + foreach (Variable v in varsToHavoc) + { + Contract.Assert(v != null); + IdentifierExpr ie = new IdentifierExpr(Token.NoToken, v); + if (!havocExprs.Contains(ie)) + havocExprs.Add(ie); + } + // pass the token of the enclosing loop header to the HavocCmd so we can reconstruct + // the source location for this later on + HavocCmd hc = new HavocCmd(header.tok, havocExprs); + List<Cmd> newCmds = new List<Cmd>(); + newCmds.Add(hc); + foreach (Cmd c in header.Cmds) + { + newCmds.Add(c); + } + header.Cmds = newCmds; + #endregion + } + #endregion + #endregion Convert program CFG into a DAG + } + + public static List<Variable> VarsAssignedInLoop(Graph<Block> g, Block header) + { + List<Variable> varsToHavoc = new List<Variable>(); + foreach (Block backEdgeNode in cce.NonNull(g.BackEdgeNodes(header))) + { + Contract.Assert(backEdgeNode != null); + foreach (Block b in g.NaturalLoops(header, backEdgeNode)) + { + Contract.Assert(b != null); + foreach (Cmd c in b.Cmds) + { + Contract.Assert(c != null); + c.AddAssignedVariables(varsToHavoc); + } + } + } + return varsToHavoc; + } + + public static IEnumerable<Variable> VarsReferencedInLoop(Graph<Block> g, Block header) + { + HashSet<Variable> referencedVars = new HashSet<Variable>(); + foreach (Block backEdgeNode in cce.NonNull(g.BackEdgeNodes(header))) + { + Contract.Assert(backEdgeNode != null); + foreach (Block b in g.NaturalLoops(header, backEdgeNode)) + { + Contract.Assert(b != null); + foreach (Cmd c in b.Cmds) + { + Contract.Assert(c != null); + var Collector = new VariableCollector(); + Collector.Visit(c); + foreach(var v in Collector.usedVars) { + referencedVars.Add(v); + } + } + } + } + return referencedVars; + } + + private void ConvertCFG2DAGKInduction(Implementation impl, Dictionary<Block, List<Block>> edgesCut, int taskID, int inductionK) { + + // K-induction has not been adapted to be aware of these parameters which standard CFG to DAG transformation uses + Contract.Requires(edgesCut == null); + Contract.Requires(taskID == -1); + Contract.Requires(0 <= inductionK); + + bool contRuleApplication = true; + while (contRuleApplication) { + contRuleApplication = false; + + #region Use the graph library to figure out where the (natural) loops are + + #region Create the graph by adding the source node and each edge + Graph<Block> g = Program.GraphFromImpl(impl); + #endregion + + g.ComputeLoops(); // this is the call that does all of the processing + if (!g.Reducible) { + throw new VCGenException("Irreducible flow graphs are unsupported."); + } + + #endregion + + foreach (Block header in cce.NonNull(g.Headers)) { + Contract.Assert(header != null); + + #region Debug Tracing + if (CommandLineOptions.Clo.TraceVerify) + { + Console.WriteLine("Applying k-induction rule with k=" + inductionK); + } + #endregion + + #region generate the step case + Block newHeader = DuplicateLoop(impl, g, header, null, + false, false, "_step_assertion"); + for (int i = 0; i < inductionK; ++i) + { + newHeader = DuplicateLoop(impl, g, header, newHeader, + true, true, + "_step_" + (inductionK - i)); + } + #endregion + + #region havoc variables that can be assigned in the loop + + List<Variable> varsToHavoc = VarsAssignedInLoop(g, header); + List<IdentifierExpr> havocExprs = new List<IdentifierExpr>(); + foreach (Variable v in varsToHavoc) + { + Contract.Assert(v != null); + IdentifierExpr ie = new IdentifierExpr(Token.NoToken, v); + if (!havocExprs.Contains(ie)) + havocExprs.Add(ie); + } + // pass the token of the enclosing loop header to the HavocCmd so we can reconstruct + // the source location for this later on + HavocCmd hc = new HavocCmd(newHeader.tok, havocExprs); + List<Cmd> havocCmds = new List<Cmd>(); + havocCmds.Add(hc); + + Block havocBlock = new Block(newHeader.tok, newHeader.Label + "_havoc", havocCmds, + new GotoCmd (newHeader.tok, new List<Block> { newHeader })); + + impl.Blocks.Add(havocBlock); + newHeader.Predecessors.Add(havocBlock); + newHeader = havocBlock; + + #endregion + + #region generate the base case loop copies + for (int i = 0; i < inductionK; ++i) + { + newHeader = DuplicateLoop(impl, g, header, newHeader, + false, false, + "_base_" + (inductionK - i)); + } + #endregion + + #region redirect into the new loop copies and remove the original loop (but don't redirect back-edges) + + IDictionary<Block, object> backEdgeNodes = new Dictionary<Block, object>(); + foreach (Block b in cce.NonNull(g.BackEdgeNodes(header))) { Contract.Assert(b != null); backEdgeNodes.Add(b, null); } + + for (int predIndex = 0, n = header.Predecessors.Count(); predIndex < n; predIndex++) + { + Block pred = cce.NonNull(header.Predecessors[predIndex]); + if (!backEdgeNodes.ContainsKey(pred)) + { + GotoCmd gc = pred.TransferCmd as GotoCmd; + Contract.Assert(gc != null); + for (int i = 0; i < gc.labelTargets.Count(); ++i) + { + if (gc.labelTargets[i] == header) + { + gc.labelTargets[i] = newHeader; + gc.labelNames[i] = newHeader.Label; + newHeader.Predecessors.Add(pred); + } + } + } + } + impl.PruneUnreachableBlocks(); + + #endregion + + contRuleApplication = true; + break; + } + + } + + ResetPredecessors(impl.Blocks); + impl.FreshenCaptureStates(); + + } + + private Block DuplicateLoop(Implementation impl, Graph<Block> g, + Block header, Block nextHeader, bool cutExits, + bool toAssumptions, string suffix) + { + IDictionary<Block, Block> ori2CopiedBlocks = new Dictionary<Block, Block>(); + Duplicator duplicator = new Duplicator(); + + #region create copies of all blocks in the loop + foreach (Block backEdgeNode in cce.NonNull(g.BackEdgeNodes(header))) + { + Contract.Assert(backEdgeNode != null); + foreach (Block b in g.NaturalLoops(header, backEdgeNode)) + { + Contract.Assert(b != null); + if (!ori2CopiedBlocks.ContainsKey(b)) + { + Block copy = (Block)duplicator.Visit(b); + copy.Cmds = new List<Cmd>(copy.Cmds); // Philipp Ruemmer commented that this was necessary due to a bug in the Duplicator. That was a long time; worth checking whether this has been fixed + copy.Predecessors = new List<Block>(); + copy.Label = copy.Label + suffix; + + #region turn asserts into assumptions + if (toAssumptions) + { + for (int i = 0; i < copy.Cmds.Count(); ++i) + { + AssertCmd ac = copy.Cmds[i] as AssertCmd; + if (ac != null) + { + copy.Cmds[i] = new AssumeCmd(ac.tok, ac.Expr); + } + } + } + #endregion + + impl.Blocks.Add(copy); + ori2CopiedBlocks.Add(b, copy); + } + } + } + #endregion + + #region adjust the transfer commands of the newly created blocks + foreach (KeyValuePair<Block, Block> pair in ori2CopiedBlocks) + { + Block copy = pair.Value; + GotoCmd gc = copy.TransferCmd as GotoCmd; + if (gc != null) + { + List<Block> newTargets = new List<Block>(); + List<string> newLabels = new List<string>(); + + for (int i = 0; i < gc.labelTargets.Count(); ++i) + { + Block newTarget; + if (gc.labelTargets[i] == header) + { + if (nextHeader != null) + { + newTargets.Add(nextHeader); + newLabels.Add(nextHeader.Label); + nextHeader.Predecessors.Add(copy); + } + } + else if (ori2CopiedBlocks.TryGetValue(gc.labelTargets[i], out newTarget)) + { + newTargets.Add(newTarget); + newLabels.Add(newTarget.Label); + newTarget.Predecessors.Add(copy); + } + else if (!cutExits) + { + newTargets.Add(gc.labelTargets[i]); + newLabels.Add(gc.labelNames[i]); + gc.labelTargets[i].Predecessors.Add(copy); + } + } + + if (newTargets.Count() == 0) + { + // if no targets are left, we assume false and return + copy.Cmds.Add(new AssumeCmd(Token.NoToken, Expr.False)); + copy.TransferCmd = new ReturnCmd(Token.NoToken); + } + else + { + copy.TransferCmd = new GotoCmd(gc.tok, newLabels, newTargets); + } + } + else if (cutExits && (copy.TransferCmd is ReturnCmd)) + { + // because return is a kind of exit from the loop, we + // assume false to cut this path + copy.Cmds.Add(new AssumeCmd(Token.NoToken, Expr.False)); + } + } + #endregion + + return ori2CopiedBlocks[header]; + } + + public void DesugarCalls(Implementation impl) { + foreach (Block block in impl.Blocks) { + List<Cmd> newCmds = new List<Cmd>(); + foreach (Cmd cmd in block.Cmds) { + SugaredCmd sugaredCmd = cmd as SugaredCmd; + if (sugaredCmd != null) { + StateCmd stateCmd = sugaredCmd.Desugaring as StateCmd; + foreach (Variable v in stateCmd.Locals) { + impl.LocVars.Add(v); + } + newCmds.AddRange(stateCmd.Cmds); + } + else { + newCmds.Add(cmd); + } + } + block.Cmds = newCmds; + } + } + + public Dictionary<TransferCmd, ReturnCmd> PassifyImpl(Implementation impl, out ModelViewInfo mvInfo) + { + Contract.Requires(impl != null); + Contract.Requires(program != null); + Contract.Ensures(Contract.Result<Dictionary<TransferCmd, ReturnCmd>>() != null); + + Dictionary<TransferCmd, ReturnCmd> gotoCmdOrigins = new Dictionary<TransferCmd, ReturnCmd>(); + Block exitBlock = GenerateUnifiedExit(impl, gotoCmdOrigins); + + #region Debug Tracing + if (CommandLineOptions.Clo.TraceVerify) + { + Console.WriteLine("after creating a unified exit block"); + EmitImpl(impl, true); + } + #endregion + + #region Insert pre- and post-conditions and where clauses as assume and assert statements + { + List<Cmd> cc = new List<Cmd>(); + // where clauses of global variables + lock (program.TopLevelDeclarations) + { + foreach (var gvar in program.GlobalVariables) + { + if (gvar != null && gvar.TypedIdent.WhereExpr != null) + { + Cmd c = new AssumeCmd(gvar.tok, gvar.TypedIdent.WhereExpr); + cc.Add(c); + } + } + } + // where clauses of in- and out-parameters + cc.AddRange(GetParamWhereClauses(impl)); + // where clauses of local variables + foreach (Variable lvar in impl.LocVars) {Contract.Assert(lvar != null); + if (lvar.TypedIdent.WhereExpr != null) { + Cmd c = new AssumeCmd(lvar.tok, lvar.TypedIdent.WhereExpr); + cc.Add(c); + } else if (QKeyValue.FindBoolAttribute(lvar.Attributes, "assumption")) { + cc.Add(new AssumeCmd(lvar.tok, new IdentifierExpr(lvar.tok, lvar), new QKeyValue(lvar.tok, "assumption_variable_initialization", new List<object>(), null))); + } + } + // add cc and the preconditions to new blocks preceding impl.Blocks[0] + InjectPreconditions(impl, cc); + + // append postconditions, starting in exitBlock and continuing into other blocks, if needed + InjectPostConditions(impl, exitBlock, gotoCmdOrigins); + } + #endregion + + #region Support for stratified inlining + addExitAssert(impl.Name, exitBlock); + #endregion + + + #region Debug Tracing + if (CommandLineOptions.Clo.TraceVerify) + { + Console.WriteLine("after inserting pre- and post-conditions"); + EmitImpl(impl, true); + } + #endregion + + AddBlocksBetween(impl.Blocks); + + #region Debug Tracing + if (CommandLineOptions.Clo.TraceVerify) + { + Console.WriteLine("after adding empty blocks as needed to catch join assumptions"); + EmitImpl(impl, true); + } + #endregion + + if (CommandLineOptions.Clo.LiveVariableAnalysis > 0) { + Microsoft.Boogie.LiveVariableAnalysis.ComputeLiveVariables(impl); + } + + mvInfo = new ModelViewInfo(program, impl); + Convert2PassiveCmd(impl, mvInfo); + + if (QKeyValue.FindBoolAttribute(impl.Attributes, "may_unverified_instrumentation")) + { + InstrumentWithMayUnverifiedConditions(impl, exitBlock); + } + + #region Peep-hole optimizations + if (CommandLineOptions.Clo.RemoveEmptyBlocks){ + #region Get rid of empty blocks + { + RemoveEmptyBlocksIterative(impl.Blocks); + impl.PruneUnreachableBlocks(); + } + #endregion Get rid of empty blocks + + #region Debug Tracing + if (CommandLineOptions.Clo.TraceVerify) + { + Console.WriteLine("after peep-hole optimizations"); + EmitImpl(impl, true); + } + #endregion + } + #endregion Peep-hole optimizations + + HandleSelectiveChecking(impl); + + +// #region Constant Folding +// #endregion +// #region Debug Tracing +// if (CommandLineOptions.Clo.TraceVerify) +// { +// Console.WriteLine("after constant folding"); +// EmitImpl(impl, true); +// } +// #endregion + + return gotoCmdOrigins; + } + + #region Simplified May-Unverified Analysis and Instrumentation + + static void InstrumentWithMayUnverifiedConditions(Implementation impl, Block unifiedExitBlock) + { + var q = new Queue<Block>(); + q.Enqueue(unifiedExitBlock); + var conditionOnBlockEntry = new Dictionary<Block, HashSet<Variable>>(); + while (q.Any()) + { + var block = q.Dequeue(); + + if (conditionOnBlockEntry.ContainsKey(block)) + { + continue; + } + + var gotoCmd = block.TransferCmd as GotoCmd; + if (gotoCmd != null && gotoCmd.labelTargets.Any(b => !conditionOnBlockEntry.ContainsKey(b))) + { + q.Enqueue(block); + continue; + } + + HashSet<Variable> cond = new HashSet<Variable>(); + if (gotoCmd != null) + { + var mayInstrs = new List<Block>(); + bool noInstr = true; + foreach (var succ in gotoCmd.labelTargets) + { + var c = conditionOnBlockEntry[succ]; + if (c != null) + { + mayInstrs.Add(succ); + } + else + { + noInstr = false; + } + cond = JoinVariableSets(cond, c); + } + if (!noInstr) + { + foreach (var instr in mayInstrs) + { + InstrumentWithCondition(instr, 0, conditionOnBlockEntry[instr]); + } + } + } + + for (int i = block.Cmds.Count - 1; 0 <= i; i--) + { + var cmd = block.Cmds[i]; + if (cond == null) { break; } + + var assertCmd = cmd as AssertCmd; + if (assertCmd != null) + { + var litExpr = assertCmd.Expr as LiteralExpr; + if (litExpr != null && litExpr.IsTrue) + { + continue; + } + + HashSet<Variable> vu = null; + if (assertCmd.VerifiedUnder == null) + { + vu = null; + } + else + { + HashSet<Variable> vars; + if (IsConjunctionOfAssumptionVariables(assertCmd.VerifiedUnder, out vars)) + { + vu = vars; + // TODO(wuestholz): Maybe drop the :verified_under attribute. + } + else + { + vu = null; + } + } + + if (vu == null) + { + InstrumentWithCondition(block, i + 1, cond); + } + + cond = JoinVariableSets(cond, vu); + } + } + + if (cond != null && block.Predecessors.Count == 0) + { + // TODO(wuestholz): Should we rather instrument each block? + InstrumentWithCondition(block, 0, cond); + } + + foreach (var pred in block.Predecessors) + { + q.Enqueue(pred); + } + + conditionOnBlockEntry[block] = cond; + } + } + + private static void InstrumentWithCondition(Block block, int idx, HashSet<Variable> condition) + { + var conj = Expr.BinaryTreeAnd(condition.Select(v => (Expr)new IdentifierExpr(Token.NoToken, v)).ToList()); + block.Cmds.Insert(idx, new AssumeCmd(Token.NoToken, Expr.Not(conj))); + } + + static HashSet<Variable> JoinVariableSets(HashSet<Variable> c0, HashSet<Variable> c1) + { + // We use the following lattice: + // - Top: null (i.e., true) + // - Bottom: new HashSet<Variable>() (i.e., false) + // - Other Elements: new HashSet<Variable>(...) (i.e., conjunctions of assumption variables) + + if (c0 == null || c1 == null) + { + return null; + } + var result = new HashSet<Variable>(c0); + result.UnionWith(c1); + return result; + } + + static bool IsAssumptionVariableOrIncarnation(Variable v) + { + if (QKeyValue.FindBoolAttribute(v.Attributes, "assumption")) { return true; } + var incar = v as Incarnation; + return incar == null || QKeyValue.FindBoolAttribute(incar.OriginalVariable.Attributes, "assumption"); + } + + static bool IsConjunctionOfAssumptionVariables(Expr expr, out HashSet<Variable> variables) + { + Contract.Requires(expr != null); + + variables = null; + var litExpr = expr as LiteralExpr; + if (litExpr != null && (litExpr.IsFalse || litExpr.IsTrue)) + { + if (litExpr.IsTrue) + { + variables = new HashSet<Variable>(); + } + return true; + } + + var idExpr = expr as IdentifierExpr; + if (idExpr != null && IsAssumptionVariableOrIncarnation(idExpr.Decl)) + { + variables = new HashSet<Variable>(); + variables.Add(idExpr.Decl); + return true; + } + + var andExpr = expr as NAryExpr; + if (andExpr != null) + { + var fun = andExpr.Fun as BinaryOperator; + if (fun != null && fun.Op == BinaryOperator.Opcode.And && andExpr.Args != null) + { + bool res = true; + variables = new HashSet<Variable>(); + foreach (var op in andExpr.Args) + { + HashSet<Variable> vars; + var r = IsConjunctionOfAssumptionVariables(op, out vars); + res &= r; + variables = JoinVariableSets(variables, vars); + if (!res) { break; } + } + return res; + } + } + + return false; + } + + #endregion + + private static void HandleSelectiveChecking(Implementation impl) + { + if (QKeyValue.FindBoolAttribute(impl.Attributes, "selective_checking") || + QKeyValue.FindBoolAttribute(impl.Proc.Attributes, "selective_checking")) { + + var startPoints = new List<Block>(); + foreach (var b in impl.Blocks) { + foreach (Cmd c in b.Cmds) { + var p = c as PredicateCmd; + if (p != null && QKeyValue.FindBoolAttribute(p.Attributes, "start_checking_here")) { + startPoints.Add(b); + break; + } + } + } + + // Compute the set of blocks reachable from blocks containing "start_checking_here" + var blocksToCheck = new HashSet<Block>(); + foreach (var b in startPoints) { + var todo = new Stack<Block>(); + var wasThere = blocksToCheck.Contains(b); + todo.Push(b); + while (todo.Count > 0) { + var x = todo.Pop(); + if (blocksToCheck.Contains(x)) continue; + blocksToCheck.Add(x); + var ex = x.TransferCmd as GotoCmd; + if (ex != null) + foreach (Block e in ex.labelTargets) + todo.Push(e); + } + if (!wasThere) blocksToCheck.Remove(b); + } + + // Convert asserts to assumes in "unreachable" blocks, as well as in portions of blocks before we reach "start_checking_here" + foreach (var b in impl.Blocks) { + if (blocksToCheck.Contains(b)) continue; // All reachable blocks must be checked in their entirety, so don't change anything + var newCmds = new List<Cmd>(); + var copyMode = false; + foreach (Cmd c in b.Cmds) { + var p = c as PredicateCmd; + if (p != null && QKeyValue.FindBoolAttribute(p.Attributes, "start_checking_here")) + copyMode = true; + var asrt = c as AssertCmd; + if (copyMode || asrt == null) + newCmds.Add(c); + else + newCmds.Add(AssertTurnedIntoAssume(asrt)); + } + + b.Cmds = newCmds; + } + } + } + + // Used by stratified inlining + protected virtual void addExitAssert(string implName, Block exitBlock) + { + } + + public virtual Counterexample extractLoopTrace(Counterexample cex, string mainProcName, Program program, Dictionary<string, Dictionary<string, Block>> extractLoopMappingInfo) + { + // Construct the set of inlined procs in the original program + var inlinedProcs = new HashSet<string>(); + foreach (var proc in program.Procedures) + { + if (!(proc is LoopProcedure)) + { + inlinedProcs.Add(proc.Name); + } + } + + return extractLoopTraceRec( + new CalleeCounterexampleInfo(cex, new List<object>()), + mainProcName, inlinedProcs, extractLoopMappingInfo).counterexample; + } + + protected CalleeCounterexampleInfo extractLoopTraceRec( + CalleeCounterexampleInfo cexInfo, string currProc, + HashSet<string> inlinedProcs, + Dictionary<string, Dictionary<string, Block>> extractLoopMappingInfo) + { + Contract.Requires(currProc != null); + if (cexInfo.counterexample == null) return cexInfo; + + var cex = cexInfo.counterexample; + // Go through all blocks in the trace, map them back to blocks in the original program (if there is one) + var ret = cex.Clone(); + ret.Trace = new List<Block>(); + ret.calleeCounterexamples = new Dictionary<TraceLocation, CalleeCounterexampleInfo>(); + + for (int numBlock = 0; numBlock < cex.Trace.Count; numBlock ++ ) + { + Block block = cex.Trace[numBlock]; + var origBlock = elGetBlock(currProc, block, extractLoopMappingInfo); + if (origBlock != null) ret.Trace.Add(origBlock); + var callCnt = 1; + for (int numInstr = 0; numInstr < block.Cmds.Count; numInstr ++) { + Cmd cmd = block.Cmds[numInstr]; + var loc = new TraceLocation(numBlock, numInstr); + if (!cex.calleeCounterexamples.ContainsKey(loc)) + { + if (getCallee(cex.getTraceCmd(loc), inlinedProcs) != null) callCnt++; + continue; + } + string callee = cex.getCalledProcName(cex.getTraceCmd(loc)); + Contract.Assert(callee != null); + var calleeTrace = cex.calleeCounterexamples[loc]; + Debug.Assert(calleeTrace != null); + + var origTrace = extractLoopTraceRec(calleeTrace, callee, inlinedProcs, extractLoopMappingInfo); + + if (elIsLoop(callee)) + { + // Absorb the trace into the current trace + + int currLen = ret.Trace.Count; + ret.Trace.AddRange(origTrace.counterexample.Trace); + + foreach (var kvp in origTrace.counterexample.calleeCounterexamples) + { + var newloc = new TraceLocation(kvp.Key.numBlock + currLen, kvp.Key.numInstr); + ret.calleeCounterexamples.Add(newloc, kvp.Value); + } + + } + else + { + var origLoc = new TraceLocation(ret.Trace.Count - 1, getCallCmdPosition(origBlock, callCnt, inlinedProcs, callee)); + ret.calleeCounterexamples.Add(origLoc, origTrace); + callCnt++; + } + } + } + return new CalleeCounterexampleInfo(ret, cexInfo.args); + } + + // return the position of the i^th CallCmd in the block (count only those Calls that call a procedure in inlinedProcs). + // Assert failure if there isn't any. + // Assert that the CallCmd found calls "callee" + private int getCallCmdPosition(Block block, int i, HashSet<string> inlinedProcs, string callee) + { + Debug.Assert(i >= 1); + for (int pos = 0; pos < block.Cmds.Count; pos++) + { + Cmd cmd = block.Cmds[pos]; + string procCalled = getCallee(cmd, inlinedProcs); + + if (procCalled != null) + { + if (i == 1) + { + Debug.Assert(procCalled == callee); + return pos; + } + i--; + } + } + + Debug.Assert(false, "Didn't find the i^th call cmd"); + return -1; + } + + private string getCallee(Cmd cmd, HashSet<string> inlinedProcs) + { + string procCalled = null; + if (cmd is CallCmd) + { + var cc = (CallCmd)cmd; + if (inlinedProcs.Contains(cc.Proc.Name)) + { + procCalled = cc.Proc.Name; + } + } + + if (cmd is AssumeCmd) + { + var expr = (cmd as AssumeCmd).Expr as NAryExpr; + if (expr != null) + { + if (inlinedProcs.Contains(expr.Fun.FunctionName)) + { + procCalled = expr.Fun.FunctionName; + } + } + } + return procCalled; + } + + protected virtual bool elIsLoop(string procname) + { + return false; + } + + private Block elGetBlock(string procname, Block block, Dictionary<string, Dictionary<string, Block>> extractLoopMappingInfo) + { + Contract.Requires(procname != null); + + if (!extractLoopMappingInfo.ContainsKey(procname)) + return block; + + if (!extractLoopMappingInfo[procname].ContainsKey(block.Label)) + return null; + + return extractLoopMappingInfo[procname][block.Label]; + } + + static Counterexample TraceCounterexample( + Block/*!*/ b, Hashtable/*!*/ traceNodes, List<Block>/*!*/ trace, Model errModel, ModelViewInfo mvInfo, + Dictionary<Incarnation, Absy/*!*/>/*!*/ incarnationOriginMap, + ProverContext/*!*/ context, + Dictionary<TraceLocation/*!*/, CalleeCounterexampleInfo/*!*/>/*!*/ calleeCounterexamples) + { + Contract.Requires(b != null); + Contract.Requires(traceNodes != null); + Contract.Requires(trace != null); + Contract.Requires(cce.NonNullDictionaryAndValues(incarnationOriginMap)); + Contract.Requires(context != null); + Contract.Requires(cce.NonNullDictionaryAndValues(calleeCounterexamples)); + // After translation, all potential errors come from asserts. + + while (true) + { + List<Cmd> cmds = b.Cmds; + Contract.Assert(cmds != null); + TransferCmd transferCmd = cce.NonNull(b.TransferCmd); + for (int i = 0; i < cmds.Count; i++) + { + Cmd cmd = cce.NonNull(cmds[i]); + + // Skip if 'cmd' not contained in the trace or not an assert + if (cmd is AssertCmd && traceNodes.Contains(cmd)) + { + Counterexample newCounterexample = AssertCmdToCounterexample((AssertCmd)cmd, transferCmd, trace, errModel, mvInfo, context); + Contract.Assert(newCounterexample != null); + newCounterexample.AddCalleeCounterexample(calleeCounterexamples); + return newCounterexample; + } + } + + GotoCmd gotoCmd = transferCmd as GotoCmd; + if (gotoCmd == null) return null; + Block foundBlock = null; + foreach (Block bb in cce.NonNull(gotoCmd.labelTargets)) + { + Contract.Assert(bb != null); + if (traceNodes.Contains(bb)) + { + foundBlock = bb; + break; + } + } + if (foundBlock == null) return null; + trace.Add(foundBlock); + b = foundBlock; + } + } + + public static Counterexample AssertCmdToCounterexample(AssertCmd cmd, TransferCmd transferCmd, List<Block> trace, Model errModel, ModelViewInfo mvInfo, ProverContext context) + { + Contract.Requires(cmd != null); + Contract.Requires(transferCmd != null); + Contract.Requires(trace != null); + Contract.Requires(context != null); + Contract.Ensures(Contract.Result<Counterexample>() != null); + + List<string> relatedInformation = new List<string>(); + + // See if it is a special assert inserted in translation + if (cmd is AssertRequiresCmd) + { + AssertRequiresCmd assertCmd = (AssertRequiresCmd)cmd; + Contract.Assert(assertCmd != null); + CallCounterexample cc = new CallCounterexample(trace, assertCmd.Call, assertCmd.Requires, errModel, mvInfo, context, assertCmd.Checksum); + cc.relatedInformation = relatedInformation; + return cc; + } + else if (cmd is AssertEnsuresCmd) + { + AssertEnsuresCmd assertCmd = (AssertEnsuresCmd)cmd; + Contract.Assert(assertCmd != null); + ReturnCounterexample rc = new ReturnCounterexample(trace, transferCmd, assertCmd.Ensures, errModel, mvInfo, context, cmd.Checksum); + rc.relatedInformation = relatedInformation; + return rc; + } + else + { + AssertCounterexample ac = new AssertCounterexample(trace, (AssertCmd)cmd, errModel, mvInfo, context); + ac.relatedInformation = relatedInformation; + return ac; + } + } + + /// <summary> + /// Returns a clone of "cex", but with the location stored in "cex" replaced by those from "assrt". + /// </summary> + public static Counterexample AssertCmdToCloneCounterexample(AssertCmd assrt, Counterexample cex) { + Contract.Requires(assrt != null); + Contract.Requires(cex != null); + Contract.Ensures(Contract.Result<Counterexample>() != null); + + List<string> relatedInformation = new List<string>(); + + Counterexample cc; + if (assrt is AssertRequiresCmd) { + var aa = (AssertRequiresCmd)assrt; + cc = new CallCounterexample(cex.Trace, aa.Call, aa.Requires, cex.Model, cex.MvInfo, cex.Context, aa.Checksum); + } else if (assrt is AssertEnsuresCmd && cex is ReturnCounterexample) { + var aa = (AssertEnsuresCmd)assrt; + var oldCex = (ReturnCounterexample)cex; + cc = new ReturnCounterexample(cex.Trace, oldCex.FailingReturn, aa.Ensures, cex.Model, cex.MvInfo, cex.Context, aa.Checksum); + } else { + cc = new AssertCounterexample(cex.Trace, assrt, cex.Model, cex.MvInfo, cex.Context); + } + cc.relatedInformation = relatedInformation; + return cc; + } + + static VCExpr LetVC(Block startBlock, + VCExpr controlFlowVariableExpr, + Dictionary<int, Absy> label2absy, + ProverContext proverCtxt, + out int assertionCount) { + Contract.Requires(startBlock != null); + Contract.Requires(proverCtxt != null); + + Contract.Ensures(Contract.Result<VCExpr>() != null); + + Hashtable/*<Block, LetVariable!>*/ blockVariables = new Hashtable/*<Block, LetVariable!!>*/(); + List<VCExprLetBinding> bindings = new List<VCExprLetBinding>(); + VCExpr startCorrect = LetVC(startBlock, controlFlowVariableExpr, label2absy, blockVariables, bindings, proverCtxt, out assertionCount); + return proverCtxt.ExprGen.Let(bindings, startCorrect); + } + + static VCExpr LetVCIterative(List<Block> blocks, + VCExpr controlFlowVariableExpr, + Dictionary<int, Absy> label2absy, + ProverContext proverCtxt, + out int assertionCount, + bool isPositiveContext = true) + { + Contract.Requires(blocks != null); + Contract.Requires(proverCtxt != null); + Contract.Ensures(Contract.Result<VCExpr>() != null); + + assertionCount = 0; + + Graph<Block> dag = new Graph<Block>(); + dag.AddSource(blocks[0]); + foreach (Block b in blocks) { + GotoCmd gtc = b.TransferCmd as GotoCmd; + if (gtc != null) { + Contract.Assume(gtc.labelTargets != null); + foreach (Block dest in gtc.labelTargets) { + Contract.Assert(dest != null); + dag.AddEdge(dest, b); + } + } + } + IEnumerable sortedNodes = dag.TopologicalSort(); + Contract.Assert(sortedNodes != null); + + Dictionary<Block, VCExprVar> blockVariables = new Dictionary<Block, VCExprVar>(); + List<VCExprLetBinding> bindings = new List<VCExprLetBinding>(); + VCExpressionGenerator gen = proverCtxt.ExprGen; + Contract.Assert(gen != null); + foreach (Block block in sortedNodes) { + VCExpr SuccCorrect; + GotoCmd gotocmd = block.TransferCmd as GotoCmd; + if (gotocmd == null) { + ReturnExprCmd re = block.TransferCmd as ReturnExprCmd; + if (re == null) { + SuccCorrect = VCExpressionGenerator.True; + } + else { + SuccCorrect = proverCtxt.BoogieExprTranslator.Translate(re.Expr); + if (isPositiveContext) + { + SuccCorrect = gen.Not(SuccCorrect); + } + } + } + else { + Contract.Assert(gotocmd.labelTargets != null); + List<VCExpr> SuccCorrectVars = new List<VCExpr>(gotocmd.labelTargets.Count); + foreach (Block successor in gotocmd.labelTargets) { + Contract.Assert(successor != null); + VCExpr s = blockVariables[successor]; + if (controlFlowVariableExpr != null) { + VCExpr controlFlowFunctionAppl = gen.ControlFlowFunctionApplication(controlFlowVariableExpr, gen.Integer(BigNum.FromInt(block.UniqueId))); + VCExpr controlTransferExpr = gen.Eq(controlFlowFunctionAppl, gen.Integer(BigNum.FromInt(successor.UniqueId))); + s = gen.Implies(controlTransferExpr, s); + } + SuccCorrectVars.Add(s); + } + SuccCorrect = gen.NAry(VCExpressionGenerator.AndOp, SuccCorrectVars); + } + + VCContext context = new VCContext(label2absy, proverCtxt, controlFlowVariableExpr, isPositiveContext); + VCExpr vc = Wlp.Block(block, SuccCorrect, context); + assertionCount += context.AssertionCount; + + VCExprVar v = gen.Variable(block.Label + "_correct", Bpl.Type.Bool); + bindings.Add(gen.LetBinding(v, vc)); + blockVariables.Add(block, v); + } + + return proverCtxt.ExprGen.Let(bindings, blockVariables[blocks[0]]); + } + + static VCExpr LetVC(Block block, + VCExpr controlFlowVariableExpr, + Dictionary<int, Absy> label2absy, + Hashtable/*<Block, VCExprVar!>*/ blockVariables, + List<VCExprLetBinding/*!*/>/*!*/ bindings, + ProverContext proverCtxt, + out int assertionCount) + { + Contract.Requires(block != null); + Contract.Requires(blockVariables!= null); + Contract.Requires(cce.NonNullElements(bindings)); + Contract.Requires(proverCtxt != null); + + Contract.Ensures(Contract.Result<VCExpr>() != null); + + assertionCount = 0; + + VCExpressionGenerator gen = proverCtxt.ExprGen; + Contract.Assert(gen != null); + VCExprVar v = (VCExprVar)blockVariables[block]; + if (v == null) { + /* + * For block A (= block), generate: + * LET_binding A_correct = wp(A_body, (/\ S \in Successors(A) :: S_correct)) + * with the side effect of adding the let bindings to "bindings" for any + * successor not yet visited. + */ + VCExpr SuccCorrect; + GotoCmd gotocmd = block.TransferCmd as GotoCmd; + if (gotocmd == null) { + ReturnExprCmd re = block.TransferCmd as ReturnExprCmd; + if (re == null) { + SuccCorrect = VCExpressionGenerator.True; + } else { + SuccCorrect = proverCtxt.BoogieExprTranslator.Translate(re.Expr); + } + } else { + Contract.Assert( gotocmd.labelTargets != null); + List<VCExpr> SuccCorrectVars = new List<VCExpr>(gotocmd.labelTargets.Count); + foreach (Block successor in gotocmd.labelTargets) { + Contract.Assert(successor != null); + int ac; + VCExpr s = LetVC(successor, controlFlowVariableExpr, label2absy, blockVariables, bindings, proverCtxt, out ac); + assertionCount += ac; + if (controlFlowVariableExpr != null) + { + VCExpr controlFlowFunctionAppl = gen.ControlFlowFunctionApplication(controlFlowVariableExpr, gen.Integer(BigNum.FromInt(block.UniqueId))); + VCExpr controlTransferExpr = gen.Eq(controlFlowFunctionAppl, gen.Integer(BigNum.FromInt(successor.UniqueId))); + s = gen.Implies(controlTransferExpr, s); + } + SuccCorrectVars.Add(s); + } + SuccCorrect = gen.NAry(VCExpressionGenerator.AndOp, SuccCorrectVars); + } + + + VCContext context = new VCContext(label2absy, proverCtxt, controlFlowVariableExpr); + VCExpr vc = Wlp.Block(block, SuccCorrect, context); + assertionCount += context.AssertionCount; + + v = gen.Variable(block.Label + "_correct", Bpl.Type.Bool); + bindings.Add(gen.LetBinding(v, vc)); + blockVariables.Add(block, v); + } + return v; + } + + static VCExpr DagVC(Block block, + VCExpr controlFlowVariableExpr, + Dictionary<int, Absy> label2absy, + Hashtable/*<Block, VCExpr!>*/ blockEquations, + ProverContext proverCtxt, + out int assertionCount) + { + Contract.Requires(block != null); + Contract.Requires(label2absy != null); + Contract.Requires(blockEquations != null); + Contract.Requires(proverCtxt != null); + Contract.Ensures(Contract.Result<VCExpr>() != null); + + assertionCount = 0; + VCExpressionGenerator gen = proverCtxt.ExprGen; + Contract.Assert(gen != null); + VCExpr vc = (VCExpr)blockEquations[block]; + if (vc != null) { + return vc; + } + + /* + * For block A (= block), generate: + * wp(A_body, (/\ S \in Successors(A) :: DagVC(S))) + */ + VCExpr SuccCorrect = null; + GotoCmd gotocmd = block.TransferCmd as GotoCmd; + if (gotocmd != null) + { + foreach (Block successor in cce.NonNull(gotocmd.labelTargets)) { + Contract.Assert(successor != null); + int ac; + VCExpr c = DagVC(successor, controlFlowVariableExpr, label2absy, blockEquations, proverCtxt, out ac); + assertionCount += ac; + if (controlFlowVariableExpr != null) { + VCExpr controlFlowFunctionAppl = gen.ControlFlowFunctionApplication(controlFlowVariableExpr, gen.Integer(BigNum.FromInt(block.UniqueId))); + VCExpr controlTransferExpr = gen.Eq(controlFlowFunctionAppl, gen.Integer(BigNum.FromInt(successor.UniqueId))); + c = gen.Implies(controlTransferExpr, c); + } + SuccCorrect = SuccCorrect == null ? c : gen.And(SuccCorrect, c); + } + } + if (SuccCorrect == null) { + SuccCorrect = VCExpressionGenerator.True; + } + + VCContext context = new VCContext(label2absy, proverCtxt, controlFlowVariableExpr); + vc = Wlp.Block(block, SuccCorrect, context); + assertionCount += context.AssertionCount; + + // gen.MarkAsSharedFormula(vc); PR: don't know yet what to do with this guy + + blockEquations.Add(block, vc); + return vc; + } + + static VCExpr FlatBlockVC(Implementation impl, + Dictionary<int, Absy> label2absy, + bool local, bool reach, bool doomed, + ProverContext proverCtxt, + out int assertionCount) + { + Contract.Requires(impl != null); + Contract.Requires(label2absy != null); + Contract.Requires(proverCtxt != null); + Contract.Requires( !local || !reach); // "reach" must be false for local + + VCExpressionGenerator gen = proverCtxt.ExprGen; + Contract.Assert(gen != null); + Hashtable/* Block --> VCExprVar */ BlkCorrect = BlockVariableMap(impl.Blocks, "_correct", gen); + Hashtable/* Block --> VCExprVar */ BlkReached = reach ? BlockVariableMap(impl.Blocks, "_reached", gen) : null; + + List<Block> blocks = impl.Blocks; + Contract.Assert(blocks != null); + // block sorting is now done on the VCExpr + // if (!local && (cce.NonNull(CommandLineOptions.Clo.TheProverFactory).NeedsBlockSorting) { + // blocks = SortBlocks(blocks); + // } + + VCExpr proofObligation; + if (!local) { + proofObligation = cce.NonNull((VCExprVar)BlkCorrect[impl.Blocks[0]]); + } else { + List<VCExpr> conjuncts = new List<VCExpr>(blocks.Count); + foreach (Block b in blocks) {Contract.Assert(b != null); + VCExpr v = cce.NonNull((VCExprVar)BlkCorrect[b]); + conjuncts.Add(v); + } + proofObligation = gen.NAry(VCExpressionGenerator.AndOp, conjuncts); + } + + VCContext context = new VCContext(label2absy, proverCtxt); + Contract.Assert(context != null); + + List<VCExprLetBinding> programSemantics = new List<VCExprLetBinding>(blocks.Count); + foreach (Block b in blocks) {Contract.Assert(b != null); + /* + * In block mode, + * For a return block A, generate: + * A_correct <== wp(A_body, true) [post-condition has been translated into an assert] + * For all other blocks, generate: + * A_correct <== wp(A_body, (/\ S \in Successors(A) :: S_correct)) + * + * In doomed mode, proceed as in block mode, except for a return block A, generate: + * A_correct <== wp(A_body, false) [post-condition has been translated into an assert] + * + * In block reach mode, the wp(A_body,...) in the equations above change to: + * A_reached ==> wp(A_body,...) + * and the conjunction above changes to: + * (/\ S \in Successors(A) :: S_correct \/ (\/ T \in Successors(A) && T != S :: T_reached)) + * + * In local mode, generate: + * A_correct <== wp(A_body, true) + */ + VCExpr SuccCorrect; + if (local) { + SuccCorrect = VCExpressionGenerator.True; + } else { + SuccCorrect = SuccessorsCorrect(b, BlkCorrect, BlkReached, doomed, gen); + } + + VCExpr wlp = Wlp.Block(b, SuccCorrect, context); + if (BlkReached != null) { + wlp = gen.Implies(cce.NonNull((VCExprVar)BlkReached[b]), wlp); + } + + VCExprVar okVar = cce.NonNull((VCExprVar)BlkCorrect[b]); + VCExprLetBinding binding = gen.LetBinding(okVar, wlp); + programSemantics.Add(binding); + } + + assertionCount = context.AssertionCount; + return gen.Let(programSemantics, proofObligation); + } + + private static Hashtable/* Block --> VCExprVar */ BlockVariableMap(List<Block/*!*/>/*!*/ blocks, string suffix, + Microsoft.Boogie.VCExpressionGenerator gen) { + Contract.Requires(cce.NonNullElements(blocks)); + Contract.Requires(suffix != null); + Contract.Requires(gen != null); + Contract.Ensures(Contract.Result<Hashtable>() != null); + + Hashtable/* Block --> VCExprVar */ map = new Hashtable/* Block --> (Let)Variable */(); + foreach (Block b in blocks) { + Contract.Assert(b != null); + VCExprVar v = gen.Variable(b.Label + suffix, Bpl.Type.Bool); + Contract.Assert(v != null); + map.Add(b, v); + } + return map; + } + + private static VCExpr SuccessorsCorrect( + Block b, + Hashtable/* Block --> VCExprVar */ BlkCorrect, + Hashtable/* Block --> VCExprVar */ BlkReached, + bool doomed, + Microsoft.Boogie.VCExpressionGenerator gen) { + Contract.Requires(b != null); + Contract.Requires(BlkCorrect != null); + Contract.Requires(gen != null); + Contract.Ensures(Contract.Result<VCExpr>() != null); + + VCExpr SuccCorrect = null; + GotoCmd gotocmd = b.TransferCmd as GotoCmd; + if (gotocmd != null) { + foreach (Block successor in cce.NonNull(gotocmd.labelTargets)) { + Contract.Assert(successor != null); + // c := S_correct + VCExpr c = (VCExprVar)BlkCorrect[successor]; + Contract.Assert(c != null); + if (BlkReached != null) { + // c := S_correct \/ Sibling0_reached \/ Sibling1_reached \/ ...; + foreach (Block successorSibling in gotocmd.labelTargets) { + Contract.Assert(successorSibling != null); + if (successorSibling != successor) { + c = gen.Or(c, cce.NonNull((VCExprVar)BlkReached[successorSibling])); + } + } + } + SuccCorrect = SuccCorrect == null ? c : gen.And(SuccCorrect, c); + } + } + if (SuccCorrect == null) { + return VCExpressionGenerator.True; + } else if (doomed) { + return VCExpressionGenerator.False; + } else { + return SuccCorrect; + } + } + + static VCExpr NestedBlockVC(Implementation impl, + Dictionary<int, Absy> label2absy, + bool reach, + ProverContext proverCtxt, + out int assertionCount){ + Contract.Requires(impl != null); + Contract.Requires(label2absy != null); + Contract.Requires(proverCtxt != null); + Contract.Requires( impl.Blocks.Count != 0); + Contract.Ensures(Contract.Result<VCExpr>() != null); + + VCExpressionGenerator gen = proverCtxt.ExprGen; + Contract.Assert(gen != null); + Graph<Block> g = Program.GraphFromImpl(impl); + + Hashtable/* Block --> VCExprVar */ BlkCorrect = BlockVariableMap(impl.Blocks, "_correct", gen); + Hashtable/* Block --> VCExprVar */ BlkReached = reach ? BlockVariableMap(impl.Blocks, "_reached", gen) : null; + + Block startBlock = cce.NonNull( impl.Blocks[0]); + VCExpr proofObligation = (VCExprVar)BlkCorrect[startBlock]; + Contract.Assert(proofObligation != null); + VCContext context = new VCContext(label2absy, proverCtxt); + + Hashtable/*Block->int*/ totalOrder = new Hashtable/*Block->int*/(); + { + List<Block> blocks = impl.Blocks; + + // block sorting is now done on the VCExpr + // if (((!)CommandLineOptions.Clo.TheProverFactory).NeedsBlockSorting) { + // blocks = SortBlocks(blocks); + // } + int i = 0; + foreach (Block b in blocks) { + Contract.Assert(b != null); + totalOrder[b] = i; + i++; + } + } + + VCExprLetBinding programSemantics = NestedBlockEquation(cce.NonNull(impl.Blocks[0]), BlkCorrect, BlkReached, totalOrder, context, g, gen); + List<VCExprLetBinding> ps = new List<VCExprLetBinding>(1); + ps.Add(programSemantics); + + assertionCount = context.AssertionCount; + return gen.Let(ps, proofObligation); + } + + private static VCExprLetBinding NestedBlockEquation(Block b, + Hashtable/*Block-->VCExprVar*/ BlkCorrect, + Hashtable/*Block-->VCExprVar*/ BlkReached, + Hashtable/*Block->int*/ totalOrder, + VCContext context, + Graph<Block> g, + Microsoft.Boogie.VCExpressionGenerator gen) { + Contract.Requires(b != null); + Contract.Requires(BlkCorrect != null); + Contract.Requires(totalOrder != null); + Contract.Requires(g != null); + Contract.Requires(context != null); + + Contract.Ensures(Contract.Result<VCExprLetBinding>() != null); + + /* + * For a block b, return: + * LET_BINDING b_correct = wp(b_body, X) + * where X is: + * LET (THOSE d \in DirectDominates(b) :: BlockEquation(d)) + * IN (/\ s \in Successors(b) :: s_correct) + * + * When the VC-expression generator does not support LET expresions, this + * will eventually turn into: + * b_correct <== wp(b_body, X) + * where X is: + * (/\ s \in Successors(b) :: s_correct) + * <== + * (/\ d \in DirectDominatees(b) :: BlockEquation(d)) + * + * In both cases above, if BlkReached is non-null, then the wp expression + * is instead: + * b_reached ==> wp(b_body, X) + */ + + VCExpr SuccCorrect = SuccessorsCorrect(b, BlkCorrect, null, false, gen); + Contract.Assert(SuccCorrect != null); + + List<VCExprLetBinding> bindings = new List<VCExprLetBinding>(); + foreach (Block dominee in GetSortedBlocksImmediatelyDominatedBy(g, b, totalOrder)) { + Contract.Assert(dominee != null); + VCExprLetBinding c = NestedBlockEquation(dominee, BlkCorrect, BlkReached, totalOrder, context, g, gen); + bindings.Add(c); + } + + VCExpr X = gen.Let(bindings, SuccCorrect); + VCExpr wlp = Wlp.Block(b, X, context); + if (BlkReached != null) { + wlp = gen.Implies((VCExprVar)BlkReached[b], wlp); + Contract.Assert(wlp != null); + } + VCExprVar okVar = cce.NonNull((VCExprVar)BlkCorrect[b]); + return gen.LetBinding(okVar, wlp); + } + + /// <summary> + /// Returns a list of g.ImmediatelyDominatedBy(b), but in a sorted order, hoping to steer around + /// the nondeterminism problems we've been seeing by using just this call. + /// </summary> + static List<Block/*!*/>/*!*/ GetSortedBlocksImmediatelyDominatedBy(Graph<Block>/*!*/ g, Block/*!*/ b, Hashtable/*Block->int*//*!*/ totalOrder) { + Contract.Requires(g != null); + Contract.Requires(b != null); + Contract.Requires(totalOrder != null); + Contract.Ensures(Contract.Result<List<Block>>() != null); + + List<Block> list = new List<Block>(); + foreach (Block dominee in g.ImmediatelyDominatedBy(b)) { + Contract.Assert(dominee != null); + list.Add(dominee); + } + list.Sort(new Comparison<Block>(delegate(Block x, Block y) { + return (int)cce.NonNull(totalOrder[x]) - (int)cce.NonNull(totalOrder[y]); + })); + return list; + } + + static VCExpr VCViaStructuredProgram + (Implementation impl, Dictionary<int, Absy> label2absy, + ProverContext proverCtxt, + out int assertionCount) + { + Contract.Requires(impl != null); + Contract.Requires(label2absy != null); + Contract.Requires(proverCtxt != null); + Contract.Ensures(Contract.Result<VCExpr>() != null); + + #region Convert block structure back to a "regular expression" + RE r = DAG2RE.Transform(cce.NonNull(impl.Blocks[0])); + Contract.Assert(r != null); + #endregion + + VCContext ctxt = new VCContext(label2absy, proverCtxt); + Contract.Assert(ctxt != null); + #region Send wlp(program,true) to Simplify + var vcexp = Wlp.RegExpr(r, VCExpressionGenerator.True, ctxt); + assertionCount = ctxt.AssertionCount; + return vcexp; + #endregion + } + + /// <summary> + /// Remove empty blocks reachable from the startBlock of the CFG + /// </summary> + static void RemoveEmptyBlocksIterative(List<Block> blocks) { + // postorder traversal of cfg + // noting loop heads in [keep] and + // generating token information in [renameInfo] + Block startBlock = blocks[0]; + var postorder = new List<Block>(); + var keep = new HashSet<Block>(); + var visited = new HashSet<Block>(); + var grey = new HashSet<Block>(); + var stack = new Stack<Block>(); + Dictionary<Block, Block> renameInfo = new Dictionary<Block, Block>(); + + stack.Push(startBlock); + visited.Add(startBlock); + while (stack.Count != 0) { + var curr = stack.Pop(); + if (grey.Contains(curr)) { + postorder.Add(curr); + + // generate renameInfoForStartBlock + GotoCmd gtc = curr.TransferCmd as GotoCmd; + renameInfo[curr] = null; + if (gtc == null || gtc.labelTargets == null || gtc.labelTargets.Count == 0) { + if (curr.Cmds.Count == 0 && curr.tok.IsValid) { + renameInfo[curr] = curr; + } + } else { + if (curr.Cmds.Count == 0 || curr == startBlock) { + if (curr.tok.IsValid) { + renameInfo[curr] = curr; + } else { + HashSet<Block> successorRenameInfo = new HashSet<Block>(); + foreach (Block s in gtc.labelTargets) { + if (keep.Contains(s)) { + successorRenameInfo.Add(null); + } else { + successorRenameInfo.Add(renameInfo[s]); + } + } + if (successorRenameInfo.Count == 1) { + renameInfo[curr] = successorRenameInfo.Single(); + } + } + } + } + // end generate renameInfoForStartBlock + + } else { + grey.Add(curr); + stack.Push(curr); + GotoCmd gtc = curr.TransferCmd as GotoCmd; + if (gtc == null || gtc.labelTargets == null || gtc.labelTargets.Count == 0) continue; + foreach (Block s in gtc.labelTargets) { + if (!visited.Contains(s)) { + visited.Add(s); + stack.Push(s); + } else if (grey.Contains(s) && !postorder.Contains(s)) { // s is a loop head + keep.Add(s); + } + } + } + } + keep.Add(startBlock); + + foreach (Block b in postorder) { + if (!keep.Contains(b) && b.Cmds.Count == 0) { + GotoCmd bGtc = b.TransferCmd as GotoCmd; + foreach (Block p in b.Predecessors) { + GotoCmd pGtc = p.TransferCmd as GotoCmd; + Contract.Assert(pGtc != null); + pGtc.labelTargets.Remove(b); + pGtc.labelNames.Remove(b.Label); + } + if (bGtc == null || bGtc.labelTargets == null || bGtc.labelTargets.Count == 0) { + continue; + } + + List<Block> successors = bGtc.labelTargets; + + // Try to push token information if possible + if (b.tok.IsValid && successors.Count == 1 && b != renameInfo[startBlock]) { + var s = successors.Single(); + if (!s.tok.IsValid) { + foreach (Block p in s.Predecessors) { + if (p != b) { + GotoCmd pGtc = p.TransferCmd as GotoCmd; + Contract.Assert(pGtc != null); + pGtc.labelTargets.Remove(s); + pGtc.labelNames.Remove(s.Label); + pGtc.labelTargets.Add(s); + pGtc.labelNames.Add(b.Label); + } + } + s.tok = b.tok; + s.Label = b.Label; + } + } + + foreach (Block p in b.Predecessors) { + GotoCmd pGtc = p.TransferCmd as GotoCmd; + Contract.Assert(pGtc != null); + foreach (Block s in successors) { + if (!pGtc.labelTargets.Contains(s)) { + pGtc.labelTargets.Add(s); + pGtc.labelNames.Add(s.Label); + } + } + } + } + } + + if (!startBlock.tok.IsValid && startBlock.Cmds.All(c => c is AssumeCmd)) { + if (renameInfo[startBlock] != null) { + startBlock.tok = renameInfo[startBlock].tok; + startBlock.Label = renameInfo[startBlock].Label; + } + } + + } + + /// <summary> + /// Remove the empty blocks reachable from the block. + /// It changes the visiting state of the blocks, so that if you want to visit again the blocks, you have to reset them... + /// </summary> + static List<Block> RemoveEmptyBlocks(Block b) { + Contract.Requires(b != null); + Contract.Ensures(Contract.Result<List<Block>>() != null); + + Contract.Assert(b.TraversingStatus == Block.VisitState.ToVisit); + Block renameInfo; + List<Block> retVal = removeEmptyBlocksWorker(b, true, out renameInfo); + if (renameInfo != null && !b.tok.IsValid) { + bool onlyAssumes = true; + foreach (Cmd c in b.Cmds) { + if (!(c is AssumeCmd)) { + onlyAssumes = false; + break; + } + } + if (onlyAssumes) { + b.tok = renameInfo.tok; + b.Label = renameInfo.Label; + } + } + return retVal; + } + + /// <summary> + /// For every not-yet-visited block n reachable from b, change n's successors to skip empty nodes. + /// Return the *set* of blocks reachable from b without passing through a nonempty block. + /// The target of any backedge is counted as a nonempty block. + /// If renameInfoForStartBlock is non-null, it denotes an empty block with location information, and that + /// information would be appropriate to display + /// </summary> + private static List<Block> removeEmptyBlocksWorker(Block b, bool startNode, out Block renameInfoForStartBlock) + { + Contract.Requires(b != null); + Contract.Ensures(Contract.ValueAtReturn(out renameInfoForStartBlock) == null || Contract.ValueAtReturn(out renameInfoForStartBlock).tok.IsValid); + // ensures: b in result ==> renameInfoForStartBlock == null; + + renameInfoForStartBlock = null; + List<Block> bs = new List<Block>(); + GotoCmd gtc = b.TransferCmd as GotoCmd; + + // b has no successors + if (gtc == null || gtc.labelTargets == null || gtc.labelTargets.Count == 0) + { + if (b.Cmds.Count != 0){ // only empty blocks are removed... + bs.Add(b); + } else if (b.tok.IsValid) { + renameInfoForStartBlock = b; + } + return bs; + } + else if (b.TraversingStatus == Block.VisitState.ToVisit) // if b has some successors and we have not seen it so far... + { + b.TraversingStatus = Block.VisitState.BeingVisited; + + // Before recursing down to successors, make a sobering observation: + // If b has no commands and is not the start node, then it will see + // extinction (because it will not be included in the "return setOfSuccessors" + // statement below). In that case, if b has a location, then the location + // information would be lost. Hence, make an attempt to save the location + // by pushing the location onto b's successor. This can be done if (0) b has + // exactly one successor, (1) that successor has no location of its own, and + // (2) that successor has no other predecessors. + if (b.Cmds.Count == 0 && !startNode) { + // b is about to become extinct; try to save its name and location, if possible + if (b.tok.IsValid && gtc.labelTargets.Count == 1) { + Block succ = cce.NonNull(gtc.labelTargets[0]); + if (!succ.tok.IsValid && succ.Predecessors.Count == 1) { + succ.tok = b.tok; + succ.Label = b.Label; + } + } + } + + // recursively call this method on each successor + // merge result into a *set* of blocks + HashSet<Block> mergedSuccessors = new HashSet<Block>(); + int m = 0; // in the following loop, set renameInfoForStartBlock to the value that all recursive calls agree on, if possible; otherwise, null + foreach (Block dest in gtc.labelTargets){Contract.Assert(dest != null); + Block renameInfo; + List<Block> ys = removeEmptyBlocksWorker(dest, false, out renameInfo); + Contract.Assert(ys != null); + if (m == 0) { + renameInfoForStartBlock = renameInfo; + } else if (renameInfoForStartBlock != renameInfo) { + renameInfoForStartBlock = null; + } + foreach (Block successor in ys){ + if (!mergedSuccessors.Contains(successor)) + mergedSuccessors.Add(successor); + } + m++; + } + b.TraversingStatus = Block.VisitState.AlreadyVisited; + + List<Block> setOfSuccessors = new List<Block>(); + foreach (Block d in mergedSuccessors) + setOfSuccessors.Add(d); + if (b.Cmds.Count == 0 && !startNode) { + // b is about to become extinct + if (b.tok.IsValid) { + renameInfoForStartBlock = b; + } + return setOfSuccessors; + } + // otherwise, update the list of successors of b to be the blocks in setOfSuccessors + gtc.labelTargets = setOfSuccessors; + gtc.labelNames = new List<String>(); + foreach (Block d in setOfSuccessors){ + Contract.Assert(d != null); + gtc.labelNames.Add(d.Label);} + if (!startNode) { + renameInfoForStartBlock = null; + } + return new List<Block> { b }; + } + else // b has some successors, but we are already visiting it, or we have already visited it... + { + return new List<Block> { b }; + } + } + + static void DumpMap(Hashtable /*Variable->Expr*/ map) { + Contract.Requires(map != null); + foreach (DictionaryEntry de in map) { + Variable v = (Variable)de.Key; + Contract.Assert(v != null); + Expr e = (Expr)de.Value; + Contract.Assert(e != null); + Console.Write(" "); + v.Emit(new TokenTextWriter("<console>", Console.Out, /*setTokens=*/ false, /*pretty=*/ false), 0); + Console.Write(" --> "); + e.Emit(new TokenTextWriter("<console>", Console.Out, /*setTokens=*/ false, /*pretty=*/ false)); + Console.WriteLine(); + } + } + } +} |