//-----------------------------------------------------------------------------
//
// 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;
///
/// Constructor. Initializes the theorem prover.
///
[NotDelayed]
public VCGen(Program program, string/*?*/ logFilePath, bool appendLogFile, List 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() != 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 copies = new Dictionary();
HashSet visited = new HashSet();
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(true);
DFS(initial);
}
void TopologicalSortImpl() {
Graph dag = new Graph();
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();
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() != 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(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() != 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 seq = new List();
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 GetCopiedBlocks() {
Contract.Ensures(cce.NonNullElements(Contract.Result>()));
// the order of nodes in res is random (except for the first one, being the entry)
List res = new List();
res.Add(copies[initial]);
foreach (KeyValuePair 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(), new List());
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 seq)
{
Contract.Requires(cur != null);
Contract.Requires(seq != null);
Contract.EnsuresOnThrow(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 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 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(true);
if (visited.Contains(cur))
return;
visited.Add(cur);
List seq = new List();
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 label2Absy;
VerifierCallback callback;
[ContractInvariantMethod]
void ObjectInvariant() {
Contract.Invariant(label2Absy != null);
Contract.Invariant(callback != null);
}
public ErrorHandler(Dictionary 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() != 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/*!>!*/ virtual_successors = new List();
public List/*!>!*/ virtual_predecesors = new List();
public HashSet 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 blocks;
readonly List big_blocks = new List();
readonly Dictionary/*!*/ stats = new Dictionary();
readonly int id;
static int current_id;
Block split_block;
bool assert_to_assume;
List/*!*/ assumized_branches = new List();
double score;
bool score_computed;
double total_cost;
int assertion_count;
double assertion_cost; // without multiplication by paths
Dictionary/*!*/ gotoCmdOrigins;
readonly public VCGen/*!*/ parent;
Implementation/*!*/ impl;
Dictionary/*!*/ copies = new Dictionary();
bool doing_slice;
double slice_initial_limit;
double slice_limit;
bool slice_pos;
HashSet/*!*/ protected_from_assert_to_assume = new HashSet();
HashSet/*!*/ keep_at_all = new HashSet();
// async interface
private Checker checker;
private int splitNo;
internal ErrorReporter reporter;
public Split(List/*!*/ blocks, Dictionary/*!*/ 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 = 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 saved = assumized_branches;
Contract.Assert(saved != null);
assumized_branches = new List();
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 backup = impl.Blocks;
Contract.Assert(backup != null);
impl.Blocks = blocks;
impl.Emit(new TokenTextWriter(filename, sw, 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() != 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/*!*/ ComputeReachableNodes(Block/*!*/ b) {
Contract.Requires(b != null);
Contract.Ensures(cce.NonNull(Contract.Result>()));
BlockStats s = GetBlockStats(b);
if (s.reachable_blocks != null) {
return s.reachable_blocks;
}
HashSet blocks = new HashSet();
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 saved_branches = new List();
foreach (Block b in big_blocks) {
Contract.Assert(b != null);
GotoCmd gt = b.TransferCmd as GotoCmd;
if (gt == null)
continue;
List 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 SliceCmds(Block b) {
Contract.Requires(b != null);
Contract.Ensures(Contract.Result>() != null);
List seq = b.Cmds;
Contract.Assert(seq != null);
if (!doing_slice && !ShouldAssumize(b))
return seq;
List res = new List();
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() != 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(), new List());
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() != null);
copies.Clear();
CloneBlock(blocks[0]);
List newBlocks = new List();
Dictionary newGotoCmdOrigins = new Dictionary();
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() != null);
assert_to_assume = idx == 0;
doing_slice = false;
ComputeBlockSets(true);
return DoSplit();
}
Split SliceAsserts(double limit, bool pos) {
Contract.Ensures(Contract.Result() != 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 tmp = impl.Blocks;
impl.Blocks = r.blocks;
EmitImpl(impl, false);
impl.Blocks = tmp;
*/
return r;
}
void Print() {
List 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() != null);
List trace = new List();
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();
}
public static List/*!*/ DoSplit(Split initial, double max_cost, int max) {
Contract.Requires(initial != null);
Contract.Ensures(cce.NonNullElements(Contract.Result>()));
List res = new List();
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 ss = new List();
ss.Add(s0.blocks[0]);
ss.Add(s1.blocks[0]);
try {
best.SoundnessCheck(new HashSet>(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>
{
public bool Equals(List x, List y)
{
return x == y || x.SequenceEqual(y);
}
public int GetHashCode(List 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() != 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(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();
}
}
///
/// As a side effect, updates "this.parent.CumulativeAssertionCount".
///
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 label2absy = new Dictionary();
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/*!*/>/*!*/ cache, Block/*!*/ orig, List/*!*/ copies) {
Contract.Requires(cce.NonNull(cache));
Contract.Requires(orig != null);
Contract.Requires(copies != null);
{
var t = new List { 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 newcopies = new List();
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 label2absy;
ProverContext ctx;
public CodeExprConversionClosure(Dictionary label2absy, ProverContext ctx)
{
this.label2absy = label2absy;
this.ctx = ctx;
}
public VCExpr CodeExprToVerificationCondition(CodeExpr codeExpr, Hashtable blockVariables, List bindings, bool isPositiveContext)
{
VCGen vcgen = new VCGen(new Program(), null, false, new List());
vcgen.variable2SequenceNumber = new Dictionary();
vcgen.incarnationOriginMap = new Dictionary();
vcgen.CurrentLocalVariables = codeExpr.LocVars;
ResetPredecessors(codeExpr.Blocks);
vcgen.AddBlocksBetween(codeExpr.Blocks);
Dictionary gotoCmdOrigins = vcgen.ConvertBlocks2PassiveCmd(codeExpr.Blocks, new List(), 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 boundVars = new List();
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(), vce);
}
if (isPositiveContext)
{
vce = ctx.ExprGen.Not(vce);
}
return vce;
}
}
public VCExpr GenerateVC(Implementation/*!*/ impl, VCExpr controlFlowVariableExpr, out Dictionary/*!*/ label2absy, ProverContext proverContext)
{
Contract.Requires(impl != null);
Contract.Requires(proverContext != null);
Contract.Ensures(Contract.ValueAtReturn(out label2absy) != null);
Contract.Ensures(Contract.Result() != null);
label2absy = new Dictionary();
return GenerateVCAux(impl, controlFlowVariableExpr, label2absy, proverContext);
}
public VCExpr GenerateVCAux(Implementation/*!*/ impl, VCExpr controlFlowVariableExpr, Dictionary/*!*/ label2absy, ProverContext proverContext) {
Contract.Requires(impl != null);
Contract.Requires(proverContext != null);
Contract.Ensures(Contract.Result() != 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/**/(), 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(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 newCmds = new List();
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 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> 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() { args[0], e1 }))));
return;
}
}
if (depth > 0 && call != null && call.Func != null)
{
Function cf = call.Func;
Expr body = cf.Body;
List 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 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 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