//-----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All Rights Reserved.
//
//-----------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.IO;
using Microsoft.Boogie;
using Graphing;
using AI = Microsoft.AbstractInterpretationFramework;
using System.Diagnostics.Contracts;
using Microsoft.Basetypes;
using Microsoft.Boogie.VCExprAST;
namespace VC {
public class DCGen : ConditionGeneration {
#region Attributes
private Dictionary/*!*/ m_BlockReachabilityMap;
Dictionary/*!*/ m_copiedBlocks = new Dictionary();
const string reachvarsuffix = "__ivebeenthere";
List/*!*/ m_doomedCmds = new List();
[ContractInvariantMethod]
void ObjectInvariant() {
Contract.Invariant(cce.NonNullElements(m_BlockReachabilityMap));
Contract.Invariant(cce.NonNullElements(m_copiedBlocks));
Contract.Invariant(cce.NonNullElements(m_doomedCmds));
Contract.Invariant(cce.NonNullElements(_copiedBlock));
}
#endregion
///
/// Constructor. Initializes the theorem prover.
///
public DCGen(Program program, string/*?*/ logFilePath, bool appendLogFile)
: base(program) {
Contract.Requires(program != null);
this.appendLogFile = appendLogFile;
this.logFilePath = logFilePath;
m_BlockReachabilityMap = new Dictionary();
}
private void Impl2Dot(Implementation impl, string filename) {
Contract.Requires(impl != null);
Contract.Requires(filename != null);
List nodes = new List();
List edges = new List();
string nodestyle = "[shape=box];";
foreach (Block b in impl.Blocks) {
Contract.Assert(b != null);
nodes.Add(string.Format("\"{0}\" {1}", b.Label, nodestyle));
GotoCmd gc = b.TransferCmd as GotoCmd;
if (gc != null) {
Contract.Assert(gc.labelTargets != null);
foreach (Block b_ in gc.labelTargets) {
Contract.Assert(b_ != null);
edges.Add(String.Format("\"{0}\" -> \"{1}\";", b.Label, b_.Label));
}
}
}
using (StreamWriter sw = new StreamWriter(filename)) {
sw.WriteLine(String.Format("digraph {0} {{", impl.Name));
// foreach (string! s in nodes) {
// sw.WriteLine(s);
// }
foreach (string s in edges) {
Contract.Assert(s != null);
sw.WriteLine(s);
}
sw.WriteLine("}}");
sw.Close();
}
}
private const string _copyPrefix = "Yeah";
private Block CopyImplBlocks(Block b, ref List blocklist, Block targetBlock, ref Dictionary alreadySeen) {
Contract.Requires(b != null);
Contract.Requires(targetBlock != null);
Contract.Requires(cce.NonNullElements(alreadySeen));
Contract.Requires(blocklist != null);
Contract.Ensures(Contract.ValueAtReturn(out blocklist) != null);
Contract.Ensures(cce.NonNullElements(Contract.ValueAtReturn(out alreadySeen)));
Contract.Ensures(Contract.Result() != null);
Block seen;
if (alreadySeen.TryGetValue(b, out seen)) {
Contract.Assert(seen != null);
return seen;
}
GotoCmd gc = b.TransferCmd as GotoCmd;
TransferCmd tcmd = null;
if (gc != null) {
BlockSeq bseq = new BlockSeq();
Contract.Assert(gc.labelTargets != null);
foreach (Block c in gc.labelTargets) {
Contract.Assert(c != null);
bseq.Add(CopyImplBlocks(c, ref blocklist, targetBlock, ref alreadySeen));
}
tcmd = new GotoCmd(gc.tok, bseq);
} else {
// BlockSeq! bseq_ = new BlockSeq();
// bseq_.Add(targetBlock);
Contract.Assert(b.TransferCmd != null);
// tcmd = new GotoCmd(b.TransferCmd.tok, bseq_);
tcmd = new ReturnCmd(b.TransferCmd.tok);
}
CodeCopier codeCopier = new CodeCopier();
CmdSeq cl = new CmdSeq();
foreach (Cmd _c in b.Cmds) {
Contract.Assert(_c != null);
if (!ContainsReachVariable(_c))
cl.Add(codeCopier.CopyCmd(_c));
}
Block b_ = new Block(b.tok, b.Label + _copyPrefix, cl, tcmd);
Contract.Assert(b_ != null);
blocklist.Add(b_);
alreadySeen[b] = b_;
return b_;
}
/*
After adding a copy of the implementation in front of our code
we remove all the edges leading from the copy to the original code
*/
private void RemoveArtificialGoto(Block b, Block target) {
Contract.Requires(b != null);
Contract.Requires(target != null);
GotoCmd gc = b.TransferCmd as GotoCmd;
if (gc != null) {
Contract.Assert(gc.labelTargets != null);
foreach (Block gt in gc.labelTargets) {
Contract.Assert(gt != null);
if (gt == target) {
Contract.Assert(gc.labelTargets.Length == 1);
Contract.Assert(gc.labelTargets[0] != null);
b.TransferCmd = new ReturnCmd(gc.tok);
return;
} else {
RemoveArtificialGoto(gt, target);
}
}
}
}
static public bool UseItAsDebugger = false;
// public static Implementation _tmpImpl = null; // (MsSchaef) HACK!
public static Block firstNonDebugBlock = null;
public static Block firstDebugBlock = null;
private void ModifyImplForDebugging(Implementation impl) {
Contract.Requires(impl != null);
//List backup_blocks=null;
if (UseItAsDebugger) {
#region Copy the Implementation /////////////////////
ConsoleColor col = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Magenta;
Console.WriteLine("Warning you are using the Infinite Improbability Drive!");
Console.ForegroundColor = col;
List/*!*/ blist = new List();
Dictionary tmpdict = new Dictionary();
CopyImplBlocks(impl.Blocks[0], ref blist, impl.Blocks[0], ref tmpdict);
blist.Reverse();
//_tmpImpl = new Implementation(impl.tok, impl.Name, impl.TypeParameters, impl.InParams, impl.OutParams, impl.LocVars, blist);
#endregion ////////////////////////////////////
#region Add implementation copy in front of implementation
// memorize where the original code starts
firstNonDebugBlock = impl.Blocks[0];
firstDebugBlock = blist[0];
// now add the copied program in front of the original one
blist.AddRange(impl.Blocks);
//backup_blocks = new List(impl.Blocks);
BlockSeq newbseq = new BlockSeq();
newbseq.Add(firstNonDebugBlock);
newbseq.Add(firstDebugBlock);
GotoCmd newtcmd = new GotoCmd(Token.NoToken, newbseq);
Block newfirst = new Block(Token.NoToken, "MySuperFirstBlock", new CmdSeq(), newtcmd);
impl.Blocks = new List();
impl.Blocks.Add(newfirst);
impl.Blocks.AddRange(blist);
//Impl2Dot(impl, String.Format("c:/dot/{0}_copied.dot", impl.Name) );
#endregion
}
}
void RemoveReachVars(Block b) {
Contract.Requires(b != null);
GotoCmd gc = b.TransferCmd as GotoCmd;
if (gc != null) {
CmdSeq cs = new CmdSeq();
foreach (Cmd c in b.Cmds) {
Contract.Assert(c != null);
if (!ContainsReachVariable(c))
cs.Add(c);
}
b.Cmds = cs;
foreach (Block c in gc.labelTargets) {
Contract.Assert(c != null);
if (c.Label != "GeneratedUnifiedExit") {
RemoveReachVars(c);
}
}
}
}
void RemoveLastBlock(Block b) {
Contract.Requires(b != null);
GotoCmd gc = b.TransferCmd as GotoCmd;
if (gc == null) {
//Console.WriteLine("WARNING: Check Node {0}", b.Label);
return;
}
Contract.Assert(gc != null);
Contract.Assert(gc.labelTargets != null);
BlockSeq tmp = new BlockSeq();
foreach (Block c in gc.labelTargets) {
Contract.Assert(c != null);
// Warning, we should not search by name!
if (c.Label != "GeneratedUnifiedExit") {
tmp.Add(c);
RemoveLastBlock(c);
} else {
c.Predecessors.Remove(b);
}
}
if (tmp.Length == 0) {
b.TransferCmd = new ReturnCmd(gc.tok);
} else {
b.TransferCmd = new GotoCmd(gc.tok, tmp);
}
}
void FindCopiedBlocks(Block b) {
Contract.Requires(b != null);
_copiedBlock.Add(b);
GotoCmd gc = b.TransferCmd as GotoCmd;
if (gc != null) {
Contract.Assert(gc.labelTargets != null);
foreach (Block c in gc.labelTargets) {
Contract.Assert(c != null);
FindCopiedBlocks(c);
}
}
}
private List _copiedBlock = new List();
///
/// MSchaef:
/// - remove loops and add reach variables
/// - make it a passive program
/// - compute the wlp for each block
/// - check if |= (reach=false) => wlp.S.false holds for each reach
///
///
public override Outcome VerifyImplementation(Implementation impl, Program program, VerifierCallback callback) {
Contract.Requires(impl != null);
Contract.Requires(program != null);
Contract.Requires(callback != null);
Contract.EnsuresOnThrow(true);
UseItAsDebugger = CommandLineOptions.Clo.useDoomDebug;
Stopwatch watch = new Stopwatch();
//Impl2Dot(impl, String.Format("c:/dot/{0}_raw.dot", impl.Name) );
if (CommandLineOptions.Clo.TraceVerify) {
Console.WriteLine(">>> Checking function {0} for doomed points.", impl.Name);
}
Console.WriteLine("Checking function {0} for doomed points:", impl.Name);
callback.OnProgress("doomdetector", 0, 0, 0);
watch.Reset();
watch.Start();
#region Transform the Program into loop-free passive form
variable2SequenceNumber = new Hashtable/*Variable -> int*/();
incarnationOriginMap = new Dictionary();
List cblocks = new List();
//List! orig_blocks = new List(impl.Blocks);
Dictionary copiedblocks;
impl.Blocks = DCProgramTransformer.Convert2Dag(impl, program, cblocks, out copiedblocks);
Contract.Assert(copiedblocks != null);
// List! blist = new List();
// blist.AddRange(impl.Blocks);
if (UseItAsDebugger)
ModifyImplForDebugging(impl);
ComputePredecessors(impl.Blocks);
m_BlockReachabilityMap = new Dictionary();
GenerateReachVars(impl);
if (UseItAsDebugger)
RemoveReachVars(cce.NonNull(firstDebugBlock));
PassifyProgram(impl);
#endregion
//EmitImpl(impl,false);
//Impl2Dot(impl, String.Format("c:/dot/{0}_passive.dot", impl.Name) );
// ---------------------------------------------------------------------------
if (UseItAsDebugger) {
Contract.Assert(firstNonDebugBlock != null && firstDebugBlock != null);
firstNonDebugBlock.Predecessors.Remove(impl.Blocks[0]);
firstDebugBlock.Predecessors.Remove(impl.Blocks[0]);
// impl.Blocks.Remove(impl.Blocks[0]); // remove the artificial first block
RemoveLastBlock(firstDebugBlock); // remove the goto to the unified exit
_copiedBlock.Clear();
FindCopiedBlocks(firstDebugBlock);
}
// ---------------------------------------------------------------------------
// EmitImpl(impl,false);
//Impl2Dot(impl, String.Format("c:/dot/{0}_final.dot", impl.Name) );
bool __debug = false;
watch.Stop();
if (__debug)
Console.WriteLine("Transformation takes: {0}", watch.Elapsed.ToString());
watch.Reset();
Checker checker = FindCheckerFor(impl, 1000);
Contract.Assert(checker != null);
DoomCheck dc = new DoomCheck(impl, checker);
int _totalchecks = 0;
Block b = null;
ProverInterface.Outcome outcome;
dc.ErrorHandler = new DoomErrorHandler(dc.Label2Absy, callback);
System.TimeSpan ts = watch.Elapsed;
while (dc.GetNextBlock(out b)) {
Contract.Assert(b != null);
outcome = ProverInterface.Outcome.Undetermined;
//Console.WriteLine("Checking block {0} ...",b.Label);
Variable v = null;
m_BlockReachabilityMap.TryGetValue(b, out v);
Contract.Assert(v != null);
_totalchecks++;
watch.Start();
if (!dc.CheckLabel(v, out outcome)) {
return Outcome.Inconclusive;
}
watch.Stop();
ts += watch.Elapsed;
if (__debug)
Console.WriteLine(" Time for Block {0}: {1} elapsed", b.Label, watch.Elapsed.ToString());
watch.Reset();
switch (outcome) {
case ProverInterface.Outcome.Valid: {
break;
}
case ProverInterface.Outcome.Invalid: {
break;
}
default: {
break;
}
}
}
checker.Close();
if (__debug)
Console.WriteLine("Number of Checked Blocks: {0} of {1}", _totalchecks, impl.Blocks.Count);
if (__debug)
Console.WriteLine("Total time for this method: {0}", ts.ToString());
#region Try to produce a counter example (brute force)
if (dc.DoomedSequences.Count > 0) {
ConsoleColor col = Console.ForegroundColor;
// Console.ForegroundColor = ConsoleColor.Red;
// Console.WriteLine(" {0} is DOOMED!", impl.Name);
// foreach (List bl in dc.DoomedSequences) {
// Console.Write("Doomed Blocks: ");
// foreach (Block! b_ in bl) {
// Console.Write("{0}, ", b_.Label);
// }
// Console.WriteLine();
// }
Console.ForegroundColor = col;
int counter = 1;
foreach (List bl in dc.DoomedSequences) {
Contract.Assert(bl != null);
Console.WriteLine("Doomed program point {0} of {1}", counter++, dc.DoomedSequences.Count);
dc.ErrorHandler.m_DoomedBlocks = bl;
foreach (Block b_ in bl) {
Contract.Assert(b_ != null);
if (m_BlockReachabilityMap.TryGetValue(b_, out dc.ErrorHandler.m_Reachvar))
break;
}
SearchCounterexample(impl, dc.ErrorHandler, callback);
}
//SearchCounterexample(impl, dc.ErrorHandler, callback);
Console.WriteLine("------------------------------ \n\n");
return Outcome.Errors;
}
#endregion
Console.WriteLine("------------------------------ \n\n");
return Outcome.Correct;
}
private void SearchCounterexample(Implementation impl, DoomErrorHandler errh, VerifierCallback callback) {
Contract.Requires(impl != null);
Contract.Requires(errh != null);
Contract.Requires(callback != null);
Contract.Requires(errh.m_Reachvar != null);
//if (errh.m_Reachvar==null) {
// Contract.Assert(false);throw new cce.UnreachableException();
//}
m_doomedCmds.Clear();
Dictionary cmdbackup = new Dictionary();
BruteForceCESearch(errh.m_Reachvar, impl, callback, cmdbackup, 0, impl.Blocks.Count / 2 - 1);
BruteForceCESearch(errh.m_Reachvar, impl, callback, cmdbackup, impl.Blocks.Count / 2, impl.Blocks.Count - 1);
List causals = CollectCausalStatements(impl.Blocks[0]);
foreach (Cmd c in causals) {
Contract.Assert(c != null);
GenerateErrorMessage(c, causals);
}
#region Undo all modifications
foreach (KeyValuePair kvp in cmdbackup) {
Contract.Assert(kvp.Key != null);
Contract.Assert(kvp.Value != null);
kvp.Key.Cmds = kvp.Value;
}
#endregion
}
#region Causal Statement Tree
private void GenerateErrorMessage(Cmd causalStatement, List causals) {
Contract.Requires(causalStatement != null);
Contract.Requires(cce.NonNullElements(causals));
AssumeCmd uc = causalStatement as AssumeCmd;
AssertCmd ac = causalStatement as AssertCmd;
ConsoleColor col = Console.ForegroundColor;
// Trivial case. Must be either assume or assert false
if (m_doomedCmds.Count == 1) {
Console.WriteLine("Found a trivial error:");
if (uc != null) {
Console.Write("Trivial false assumption: ");
Console.Write("({0};{1}):", uc.tok.line, uc.tok.col);
}
if (ac != null) {
Console.Write("Trivial false assertion: ");
Console.Write("({0};{1}):", ac.tok.line, ac.tok.col);
}
causalStatement.Emit(new TokenTextWriter("", Console.Out, false), 0);
} else {
// Safety error
if (ac != null) {
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Safety error:");
Console.ForegroundColor = col;
Console.Write("This assertion is violated: ");
Console.Write("({0};{1}):", ac.tok.line, ac.tok.col);
ac.Emit(new TokenTextWriter("", Console.Out, false), 0);
}
if (uc != null) {
bool containsAssert = false;
foreach (Cmd c in m_doomedCmds) {
Contract.Assert(c != null);
if (causals.Contains(c)) {
continue;
}
AssertCmd asrt = c as AssertCmd;
if (asrt != null) {
containsAssert = true;
break;
}
}
// Plausibility error
if (containsAssert) {
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Plausibility error:");
Console.ForegroundColor = col;
Console.Write("There is no legal exeuction passing: ");
Console.Write("({0};{1})", uc.tok.line, uc.tok.col);
uc.Emit(new TokenTextWriter("", Console.Out, false), 0);
} else { // Reachability error
Console.ForegroundColor = ConsoleColor.DarkRed;
Console.WriteLine("Reachability error:");
Console.ForegroundColor = col;
Console.Write("No execution can reach: ");
Console.Write("({0};{1})", uc.tok.line, uc.tok.col);
uc.Emit(new TokenTextWriter("", Console.Out, false), 0);
}
}
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("...on any execution passing through:");
foreach (Cmd c in m_doomedCmds) {
Contract.Assert(c != null);
if (causals.Contains(c)) {
continue;
}
Console.ForegroundColor = col;
Console.Write("In ({0};{1}): ", c.tok.line, c.tok.col);
Console.ForegroundColor = ConsoleColor.DarkYellow;
c.Emit(new TokenTextWriter("", Console.Out, false), 0);
}
Console.ForegroundColor = col;
Console.WriteLine("--");
}
}
private List CollectCausalStatements(Block b) {
Contract.Requires(b != null);
Contract.Ensures(cce.NonNullElements(Contract.Result>()));
Cmd lastCausal = null;
foreach (Cmd c in b.Cmds) {
Contract.Assert(c != null);
AssertCmd ac = c as AssertCmd;
AssumeCmd uc = c as AssumeCmd;
if (ac != null && !ContainsReachVariable(ac)) {
if (ac.Expr != Expr.True) {
lastCausal = c;
}
} else if (uc != null && !ContainsReachVariable(uc)) {
lastCausal = c;
}
}
List causals = new List();
GotoCmd gc = b.TransferCmd as GotoCmd;
if (gc != null && gc.labelTargets != null) {
List tmp;
//bool allcausal = true;
foreach (Block b_ in gc.labelTargets) {
Contract.Assert(b_ != null);
tmp = CollectCausalStatements(b_);
foreach (Cmd cau in tmp) {
if (!causals.Contains(cau))
causals.Add(cau);
}
}
//if (allcausal)
if (causals.Count > 0)
return causals;
}
if (lastCausal != null)
causals.Add(lastCausal);
return causals;
}
#endregion
bool BruteForceCESearch(Variable reachvar, Implementation impl, VerifierCallback callback,
Dictionary cmdbackup, int startidx, int endidx) {
Contract.Requires(reachvar != null);
Contract.Requires(impl != null);
Contract.Requires(callback != null);
Contract.Requires(cce.NonNullElements(cmdbackup));
#region Modify implementation
for (int i = startidx; i <= endidx; i++) {
if (_copiedBlock.Contains(impl.Blocks[i]))
continue;
CmdSeq cs = new CmdSeq();
cmdbackup.Add(impl.Blocks[i], impl.Blocks[i].Cmds);
foreach (Cmd c in impl.Blocks[i].Cmds) {
Contract.Assert(c != null);
if (ContainsReachVariable(c)) {
cs.Add(c);
continue;
}
AssertCmd ac = c as AssertCmd;
AssumeCmd uc = c as AssumeCmd;
if (ac != null) {
cs.Add(new AssertCmd(ac.tok, Expr.True));
} else if (uc != null) {
cs.Add(new AssertCmd(uc.tok, Expr.True));
} else {
cs.Add(c);
}
}
impl.Blocks[i].Cmds = cs;
}
#endregion
ProverInterface.Outcome outcome = ProverInterface.Outcome.Undetermined;
if (!ReCheckImpl(reachvar, impl, callback, out outcome)) {
UndoBlockModifications(impl, cmdbackup, startidx, endidx);
return false;
}
if (outcome == ProverInterface.Outcome.Valid) {
return true;
} else if (outcome == ProverInterface.Outcome.Invalid) {
UndoBlockModifications(impl, cmdbackup, startidx, endidx);
int mid = startidx + (endidx - startidx) / 2;
if (startidx >= endidx) {
// Now we found an interesting Block and we have to
// search for the interesting statements.
int cmdcount = impl.Blocks[endidx].Cmds.Length;
BruteForceCmd(impl.Blocks[endidx], 0, cmdcount / 2 - 1, reachvar, impl, callback);
BruteForceCmd(impl.Blocks[endidx], cmdcount / 2, cmdcount - 1, reachvar, impl, callback);
return true;
} else {
BruteForceCESearch(reachvar, impl, callback, cmdbackup, startidx, mid);
BruteForceCESearch(reachvar, impl, callback, cmdbackup, mid + 1, endidx);
return true;
}
} else {
UndoBlockModifications(impl, cmdbackup, startidx, endidx);
return false;
}
}
bool BruteForceCmd(Block b, int startidx, int endidx, Variable reachvar,
Implementation impl, VerifierCallback callback) {
Contract.Requires(b != null);
Contract.Requires(reachvar != null);
Contract.Requires(impl != null);
Contract.Requires(callback != null);
#region Modify Cmds
CmdSeq backup = b.Cmds;
Contract.Assert(backup != null);
CmdSeq cs = new CmdSeq();
for (int i = 0; i < startidx; i++) {
cs.Add(b.Cmds[i]);
}
for (int i = startidx; i <= endidx; i++) {
Cmd c = b.Cmds[i];
if (ContainsReachVariable(c)) {
cs.Add(c);
continue;
}
cs.Add(new AssertCmd(c.tok, Expr.True));
}
for (int i = endidx + 1; i < b.Cmds.Length; i++) {
cs.Add(b.Cmds[i]);
}
b.Cmds = cs;
#endregion
#region Recheck
ProverInterface.Outcome outcome = ProverInterface.Outcome.Undetermined;
if (!ReCheckImpl(reachvar, impl, callback, out outcome)) {
b.Cmds = backup;
return false;
}
#endregion
if (outcome == ProverInterface.Outcome.Valid) {
return true;
} else if (outcome == ProverInterface.Outcome.Invalid) {
b.Cmds = backup;
if (startidx >= endidx) {
if (!ContainsReachVariable(b.Cmds[endidx])) {
// Console.Write(" Witness (");
//
// ConsoleColor col = Console.ForegroundColor;
// Console.ForegroundColor = ConsoleColor.White;
// Console.Write("{0};{1}", b.Cmds[endidx].tok.line, b.Cmds[endidx].tok.col );
// Console.ForegroundColor = col;
// Console.Write("): ");
// Console.ForegroundColor = ConsoleColor.Yellow;
// b.Cmds[endidx].Emit(new TokenTextWriter("", Console.Out, false), 0);
// Console.ForegroundColor = col;
m_doomedCmds.Add(b.Cmds[endidx]);
return true;
} else {
return false;
}
} else {
int mid = startidx + (endidx - startidx) / 2;
BruteForceCmd(b, startidx, mid, reachvar, impl, callback);
BruteForceCmd(b, mid + 1, endidx, reachvar, impl, callback);
return false; // This is pure random
}
} else {
b.Cmds = backup;
return false;
}
return false;
}
void UndoBlockModifications(Implementation impl, Dictionary/*!*/ cmdbackup,
int startidx, int endidx) {
Contract.Requires(cce.NonNullElements(cmdbackup));
Contract.Requires(impl != null);
for (int i = startidx; i <= endidx; i++) {
CmdSeq cs = null;
if (cmdbackup.TryGetValue(impl.Blocks[i], out cs)) {
Contract.Assert(cs != null);
impl.Blocks[i].Cmds = cs;
cmdbackup.Remove(impl.Blocks[i]);
}
}
}
bool ReCheckImpl(Variable reachvar, Implementation impl, VerifierCallback callback,
out ProverInterface.Outcome outcome) {
Contract.Requires(reachvar != null);
Contract.Requires(impl != null);
Contract.Requires(callback != null);
Checker checker = FindCheckerFor(impl, 1000);
Contract.Assert(checker != null);
DoomCheck dc = new DoomCheck(impl, checker);
dc.ErrorHandler = new DoomErrorHandler(dc.Label2Absy, callback);
outcome = ProverInterface.Outcome.Undetermined;
if (!dc.CheckLabel(reachvar, out outcome)) {
checker.Close();
return false;
}
checker.Close();
return true;
}
bool ContainsReachVariable(Cmd c) {
Contract.Requires(c != null);
AssertCmd artc = c as AssertCmd;
AssumeCmd amec = c as AssumeCmd;
Expr e;
if (artc != null) {
e = artc.Expr;
} else if (amec != null) {
e = amec.Expr;
} else {
return false;
}
Set freevars = new Set();
e.ComputeFreeVariables(freevars);
foreach (Variable v in freevars) {
Contract.Assert(v != null);
if (v.Name.Contains(reachvarsuffix))
return true;
}
return false;
}
#region Loop Removal
///
/// This class is accessed only via the static method Convert2Dag
/// It converts the program into a loopfree one by unrolling the loop threetimes and adding the appropriate havoc
/// statements. The first and the last unrolling represent the first and last iteration of the loop. The second
/// unrolling stands for any other iteration.
///
private class DCProgramTransformer {
private List Blocks;
private List m_checkableBlocks;
private Dictionary m_copyMap = new Dictionary();
[ContractInvariantMethod]
void ObjectInvariant() {
Contract.Invariant(cce.NonNullElements(Blocks));
Contract.Invariant(cce.NonNullElements(m_checkableBlocks));
Contract.Invariant(cce.NonNullElements(m_copyMap));
}
public static List Convert2Dag(Implementation impl, Program program, List checkableBlocks,
out Dictionary copiedblocks) {
Contract.Requires(impl != null);
Contract.Requires(program != null);
Contract.Requires(cce.NonNullElements(checkableBlocks));
Contract.Requires(1 <= impl.Blocks.Count);
Contract.Ensures(cce.NonNullElements(Contract.ValueAtReturn(out copiedblocks)));
Contract.Ensures(cce.NonNullElements(Contract.Result>()));
Block start = impl.Blocks[0];
Contract.Assert(start != null);
Dictionary gd = new Dictionary();
Set/*Block*/ beingVisited = new Set/*Block*/();
GraphNode gStart = GraphNode.ComputeGraphInfo(null, start, gd, beingVisited);
DCProgramTransformer pt = new DCProgramTransformer(checkableBlocks);
pt.LoopUnrolling(gStart, new Dictionary(), true, "");
pt.Blocks.Reverse();
copiedblocks = pt.m_copyMap;
return pt.Blocks;
}
DCProgramTransformer(List checkableBlocks) {
Contract.Requires(cce.NonNullElements(checkableBlocks));
Blocks = new List();
m_checkableBlocks = checkableBlocks;
}
#region Loop Unrolling Methods
private Block LoopUnrolling(GraphNode node, Dictionary visited, bool unrollable, String prefix) {
Contract.Requires(node != null);
Contract.Requires(cce.NonNullElements(visited));
Contract.Requires(prefix != null);
Contract.Ensures(Contract.Result() != null);
Block newb;
if (visited.TryGetValue(node, out newb)) {
Contract.Assert(newb != null);
return newb;
} else {
if (node.IsCutPoint) {
// compute the loop body and the blocks leaving the loop
List loopNodes = new List();
GatherLoopBodyNodes(node, node, loopNodes);
List exitNodes = GatherLoopExitNodes(loopNodes);
// Continue Unrolling after the current loop
Dictionary _visited = new Dictionary();
foreach (GraphNode g in exitNodes) {
Contract.Assert(g != null);
Block b = LoopUnrolling(g, visited, unrollable, prefix);
_visited.Add(g, b);
}
newb = UnrollCurrentLoop(node, _visited, loopNodes, unrollable, prefix);
visited.Add(node, newb);
} else {
BlockSeq newSuccs = new BlockSeq();
foreach (GraphNode g in node.Succecessors) {
Contract.Assert(g != null);
newSuccs.Add(LoopUnrolling(g, visited, unrollable, prefix));
}
newb = new Block(node.Block.tok, node.Block.Label + prefix, node.Body, node.Block.TransferCmd);
Block b;
if (m_copyMap.TryGetValue(node.Block, out b)) {
Contract.Assert(b != null);
m_copyMap.Add(newb, b);
} else {
m_copyMap.Add(newb, node.Block);
}
Contract.Assert(newb != null);
Contract.Assert(newb.TransferCmd != null);
if (newSuccs.Length == 0)
newb.TransferCmd = new ReturnCmd(newb.TransferCmd.tok);
else
newb.TransferCmd = new GotoCmd(newb.TransferCmd.tok, newSuccs);
visited.Add(node, newb);
Blocks.Add(newb);
if (unrollable) {
m_checkableBlocks.Add(newb);
}
}
}
Contract.Assert(newb != null);
//newb.checkable = unrollable;
return newb;
}
private Block UnrollCurrentLoop(GraphNode/*!*/ cutPoint, Dictionary/*!*/ visited,
List/*!*/ loopNodes, bool unrollable, String/*!*/ prefix) {
Contract.Requires(cutPoint != null);
Contract.Requires(cce.NonNullElements(visited));
Contract.Requires(cce.NonNullElements(loopNodes));
Contract.Requires(prefix != null);
if (unrollable) {
Dictionary visited1 = new Dictionary(visited);
Dictionary visited2 = new Dictionary(visited);
Dictionary visited3 = new Dictionary(visited);
Block loopend = ConstructLoopExitBlock(cutPoint, loopNodes, visited, prefix + "#Last");
Contract.Assert(loopend != null);
Block last = UnrollOnce(cutPoint, loopend, visited1, false, prefix + "#Last");
Contract.Assert(last != null);
AddHavocCmd(last, loopNodes);
// You might use true for the unrollable flag as well.
Block arb = UnrollOnce(cutPoint, last, visited2, false, prefix + "#Arb");
Contract.Assert(arb != null);
AddHavocCmd(arb, loopNodes);
BlockSeq succ = new BlockSeq();
succ.Add(last);
succ.Add(arb);
Contract.Assert(arb.TransferCmd != null);
Block tmp = new Block(arb.tok, arb.Label + prefix + "#Dummy", new CmdSeq(), new GotoCmd(arb.TransferCmd.tok, succ));
Contract.Assert(tmp != null);
Blocks.Add(tmp);
m_checkableBlocks.Add(tmp);
// check if arb is already a copy of something else
// if not then write to m_copyMap that tmp is a copy
// of arb
Block b = null;
if (m_copyMap.TryGetValue(arb, out b)) {
Contract.Assert(b != null);
m_copyMap.Add(tmp, b);
} else {
m_copyMap.Add(tmp, arb);
}
Block first = UnrollOnce(cutPoint, tmp, visited3, false, prefix + "#First");
Contract.Assert(first != null);
return first;
} else {
Dictionary visited_ = new Dictionary(visited);
Block loopend = AbstractIteration(cutPoint, prefix + "#UR");
Block ret = UnrollOnce(cutPoint, loopend, visited_, false, prefix);
AddHavocCmd(ret, loopNodes);
return ret;
}
}
private Block UnrollOnce(GraphNode node, Block nextIter, Dictionary visited, bool unrollable, String prefix) {
Contract.Requires(node != null);
Contract.Requires(nextIter != null);
Contract.Requires(cce.NonNullElements(visited));
Contract.Requires(prefix != null);
Contract.Ensures(Contract.Result() != null);
visited.Add(node, nextIter);
Block newb, b;
BlockSeq newSuccs = new BlockSeq();
foreach (GraphNode g in node.Succecessors) {
Contract.Assert(g != null);
newSuccs.Add(LoopUnrolling(g, visited, unrollable, prefix));
}
newb = new Block(node.Block.tok, node.Block.Label + prefix, node.Body, node.Block.TransferCmd);
if (m_copyMap.TryGetValue(node.Block, out b)) {
Contract.Assert(b != null);
m_copyMap.Add(newb, b);
} else {
m_copyMap.Add(newb, node.Block);
}
Contract.Assert(newb != null);
Contract.Assert(newb.TransferCmd != null);
if (newSuccs.Length == 0)
newb.TransferCmd = new ReturnCmd(newb.TransferCmd.tok);
else
newb.TransferCmd = new GotoCmd(newb.TransferCmd.tok, newSuccs);
Blocks.Add(newb);
if (unrollable)
m_checkableBlocks.Add(newb);
return newb;
}
private Block AbstractIteration(GraphNode node, String prefix) {
Contract.Requires(node != null);
Contract.Requires(prefix != null);
Contract.Ensures(Contract.Result() != null);
CmdSeq body = new CmdSeq();
foreach (Cmd c in node.Body) {
Contract.Assert(c != null);
if (c is PredicateCmd || c is CommentCmd)
body.Add(c);
else
break;
}
body.Add(new AssumeCmd(node.Block.tok, Expr.False));
TransferCmd tcmd = new ReturnCmd(node.Block.tok);
Contract.Assert(tcmd != null);
Block b = new Block(node.Block.tok, node.Block.Label + prefix, body, tcmd);
Contract.Assert(b != null);
Blocks.Add(b);
Block tmp;
if (m_copyMap.TryGetValue(node.Block, out tmp)) {
Contract.Assert(tmp != null);
m_copyMap.Add(b, tmp);
} else {
m_copyMap.Add(b, node.Block);
}
return b;
}
private Block ConstructLoopExitBlock(GraphNode cutPoint, List loopNodes,
Dictionary visited, String prefix) {
Contract.Requires(cutPoint != null);
Contract.Requires(cce.NonNullElements(loopNodes));
Contract.Requires(cce.NonNullElements(visited));
Contract.Requires(prefix != null);
Contract.Ensures(Contract.Result() != null);
BlockSeq newSucc = new BlockSeq();
Block orig = cutPoint.Block;
// detect the block after the loop
// FixMe: What happens when using break commands?
foreach (GraphNode g in cutPoint.Succecessors) {
Contract.Assert(g != null);
if (!loopNodes.Contains(g)) {
Block b;
if (visited.TryGetValue(g, out b))
newSucc.Add(b);
}
}
TransferCmd tcmd;
Contract.Assert(orig.TransferCmd != null);
if (newSucc.Length == 0)
tcmd = new ReturnCmd(orig.TransferCmd.tok);
else
tcmd = new GotoCmd(orig.TransferCmd.tok, newSucc);
// FixMe: Genertate IToken for counterexample creation
Block newb = new Block(orig.tok, orig.Label + prefix + "#Leave", orig.Cmds, tcmd);
Contract.Assert(newb != null);
Blocks.Add(newb);
m_checkableBlocks.Add(newb);
return newb;
}
private void GatherLoopBodyNodes(GraphNode current, GraphNode cutPoint, List loopNodes) {
Contract.Requires(current != null);
Contract.Requires(cutPoint != null);
Contract.Requires(cce.NonNullElements(loopNodes));
loopNodes.Add(current);
if (false)
System.Diagnostics.Debugger.Break();
foreach (GraphNode g in current.Predecessors) {
Contract.Assert(g != null);
if (cutPoint.firstPredecessor == g || g == cutPoint || loopNodes.Contains(g))
continue;
GatherLoopBodyNodes(g, cutPoint, loopNodes);
}
}
private List GatherLoopExitNodes(List loopNodes) {
Contract.Requires(cce.NonNullElements(loopNodes));
Contract.Ensures(cce.NonNullElements(Contract.Result>()));
List exitnodes = new List();
foreach (GraphNode g in loopNodes) {
Contract.Assert(g != null);
foreach (GraphNode s in g.Succecessors) {
Contract.Assert(s != null);
if (!loopNodes.Contains(s) /*&& !exitnodes.Contains(s)*/ )
exitnodes.Add(s);
}
}
return exitnodes;
}
private void AddHavocCmd(Block b, List loopNodes) {
Contract.Requires(b != null);
Contract.Requires(loopNodes != null);
List loopBlocks = new List();
foreach (GraphNode g in loopNodes) {
Contract.Assert(g != null);
loopBlocks.Add(g.Block);
}
HavocCmd hcmd = HavocLoopTargets(loopBlocks, b.tok);
Contract.Assert(hcmd != null);
CmdSeq body = new CmdSeq();
body.Add(hcmd);
body.AddRange(b.Cmds);
b.Cmds = body;
}
private HavocCmd HavocLoopTargets(List bl, IToken tok) {
Contract.Requires(bl != null);
Contract.Requires(tok != null);
Contract.Ensures(Contract.Result() != null);
VariableSeq varsToHavoc = new VariableSeq();
foreach (Block b in bl) {
Contract.Assert(b != null);
foreach (Cmd c in b.Cmds) {
Contract.Assert(c != null);
c.AddAssignedVariables(varsToHavoc);
}
}
IdentifierExprSeq havocExprs = new IdentifierExprSeq();
foreach (Variable v in varsToHavoc) {
Contract.Assert(v != null);
IdentifierExpr ie = new IdentifierExpr(Token.NoToken, v);
if (!havocExprs.Has(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
return new HavocCmd(tok, havocExprs);
}
#endregion
#region GraphNode
private class GraphNode {
public readonly Block Block;
public readonly CmdSeq Body;
public bool IsCutPoint; // is set during ComputeGraphInfo
[Rep]
public readonly List/*!*/ Predecessors = new List();
[Rep]
public readonly List/*!*/ Succecessors = new List();
public GraphNode firstPredecessor;
public List/*!*/ UnavoidableNodes = new List(); // should be done using a set
[ContractInvariantMethod]
void ObjectInvariant() {
Contract.Invariant(Block != null);
Contract.Invariant(Body != null);
Contract.Invariant(cce.NonNullElements(Predecessors));
Contract.Invariant(cce.NonNullElements(Succecessors));
Contract.Invariant(cce.NonNullElements(UnavoidableNodes));
}
GraphNode(Block b, CmdSeq body) {
Contract.Requires(b != null);
Contract.Requires(body != null);
Block = b;
Body = body;
IsCutPoint = false;
}
static CmdSeq GetOptimizedBody(CmdSeq cmds) {
Contract.Requires(cmds != null);
Contract.Ensures(Contract.Result() != null);
int n = 0;
foreach (Cmd c in cmds) {
n++;
PredicateCmd pc = c as PredicateCmd;
if (pc != null && pc.Expr is LiteralExpr && ((LiteralExpr)pc.Expr).IsFalse) {
// return a sequence consisting of the commands seen so far
Cmd[] s = new Cmd[n];
for (int i = 0; i < n; i++) {
s[i] = cmds[i];
}
return new CmdSeq(s);
}
}
return cmds;
}
private static List Intersect(List left, List right) {
Contract.Requires(left != null);
Contract.Requires(right != null);
Contract.Ensures(cce.NonNullElements(Contract.Result>()));
List ret = new List();
List tmp = left;
tmp.AddRange(right);
foreach (GraphNode gn in tmp) {
Contract.Assert(gn != null);
if (ret.Contains(gn))
continue;
if (left.Contains(gn) && right.Contains(gn))
ret.Add(gn);
}
return ret;
}
public static GraphNode ComputeGraphInfo(GraphNode from, Block b, Dictionary gd, Set /*Block*/ beingVisited) {
Contract.Requires(b != null);
Contract.Requires(beingVisited != null);
Contract.Requires(cce.NonNullElements(gd));
Contract.Ensures(Contract.Result() != null);
GraphNode g;
if (gd.TryGetValue(b, out g)) {
Contract.Assume(from != null);
Contract.Assert(g != null);
g.UnavoidableNodes = Intersect(g.UnavoidableNodes, from.UnavoidableNodes);
if (!g.UnavoidableNodes.Contains(g))
g.UnavoidableNodes.Add(g);
g.Predecessors.Add(from);
if (g.firstPredecessor == null)
g.firstPredecessor = from;
if (beingVisited.Contains(b))
g.IsCutPoint = true; // it's a cut point
} else {
CmdSeq body = GetOptimizedBody(b.Cmds);
g = new GraphNode(b, body);
gd.Add(b, g);
if (from != null) {
g.Predecessors.Add(from);
if (from == null)
g.firstPredecessor = g;
if (g.firstPredecessor == null)
g.firstPredecessor = from;
}
if (body != b.Cmds) {
// the body was optimized -- there is no way through this block
} else {
beingVisited.Add(b);
GotoCmd gcmd = b.TransferCmd as GotoCmd;
if (gcmd != null) {
Contract.Assume(gcmd.labelTargets != null);
foreach (Block succ in gcmd.labelTargets) {
Contract.Assert(succ != null);
g.Succecessors.Add(ComputeGraphInfo(g, succ, gd, beingVisited));
}
}
beingVisited.Remove(b);
}
}
return g;
}
}
}
#endregion
#endregion
#region Program Passification
private void GenerateReachVars(Implementation impl) {
Contract.Requires(impl != null);
Hashtable gotoCmdOrigins = new Hashtable();
Block exitBlock = GenerateUnifiedExit(impl, gotoCmdOrigins);
Contract.Assert(exitBlock != null);
AddBlocksBetween(impl.Blocks);
#region Insert pre- and post-conditions and where clauses as assume and assert statements
{
CmdSeq cc = new CmdSeq();
// where clauses of global variables
foreach (Declaration d in program.TopLevelDeclarations) {
GlobalVariable gvar = d as GlobalVariable;
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);
}
}
// 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
exitBlock = InjectPostConditions(impl, exitBlock, gotoCmdOrigins);
}
#endregion
GenerateReachabilityPredicates(impl, exitBlock);
}
private Hashtable/*TransferCmd->ReturnCmd*/ PassifyProgram(Implementation impl) {
Contract.Requires(impl != null);
Contract.Ensures(Contract.Result() != null);
CurrentLocalVariables = impl.LocVars;
Convert2PassiveCmd(impl);
return new Hashtable();
}
///
/// Add additional variable to allow checking as described in the paper
/// "It's doomed; we can prove it"
///
private void GenerateReachabilityPredicates(Implementation impl, Block exitBlock) {
Contract.Requires(impl != null);
Contract.Requires(exitBlock != null);
ExprSeq es = new ExprSeq();
Cmd eblockcond = null;
foreach (Block b in impl.Blocks) {
Contract.Assert(b != null);
//if (b.Predecessors.Length==0) continue;
//if (b.Cmds.Length == 0 ) continue;
Variable v_ = new LocalVariable(Token.NoToken,
new TypedIdent(b.tok, b.Label + reachvarsuffix, new BasicType(SimpleType.Bool)));
impl.LocVars.Add(v_);
m_BlockReachabilityMap[b] = v_;
IdentifierExpr lhs = new IdentifierExpr(b.tok, v_);
Contract.Assert(lhs != null);
es.Add(new IdentifierExpr(b.tok, v_));
List lhsl = new List();
lhsl.Add(new SimpleAssignLhs(Token.NoToken, lhs));
List rhsl = new List();
rhsl.Add(Expr.True);
if (b != exitBlock) {
CmdSeq cs = new CmdSeq(new AssignCmd(Token.NoToken, lhsl, rhsl));
cs.AddRange(b.Cmds);
b.Cmds = cs;
} else {
eblockcond = new AssignCmd(Token.NoToken, lhsl, rhsl);
}
//checkBlocks.Add(new CheckableBlock(v_,b));
}
if (es.Length == 0)
return;
Expr aexp = null;
if (es.Length == 1) {
aexp = es[0];
} else if (es.Length == 2) {
aexp = Expr.Binary(Token.NoToken,
BinaryOperator.Opcode.And,
cce.NonNull(es[0]),
cce.NonNull(es[1]));
} else {
aexp = Expr.True;
foreach (Expr e_ in es) {
aexp = Expr.Binary(Token.NoToken,
BinaryOperator.Opcode.And,
cce.NonNull(e_), aexp);
}
}
Contract.Assert(aexp != null);
Contract.Assert(eblockcond != null);
AssumeCmd ac = new AssumeCmd(Token.NoToken, aexp);
Contract.Assert(exitBlock != null);
CmdSeq cseq = new CmdSeq(eblockcond);
cseq.AddRange(exitBlock.Cmds);
cseq.Add(ac);
exitBlock.Cmds = cseq;
}
#endregion
}
}