summaryrefslogtreecommitdiff
path: root/Source/Houdini
diff options
context:
space:
mode:
authorGravatar Dan Liew <daniel.liew@imperial.ac.uk>2015-06-28 01:44:30 +0100
committerGravatar Dan Liew <daniel.liew@imperial.ac.uk>2015-06-28 01:44:30 +0100
commit962f8d5252b3f5ec4d19e0cd2a430934bd55cc6d (patch)
tree27d5f9b0d130c6c1a6758bc0b7456b0aa51e34e0 /Source/Houdini
parente11d65009d0b4ba1327f5f5dd6b26367330611f0 (diff)
Normalise line endings using a .gitattributes file. Unfortunately
this required that this commit globally modify most files. If you want to use git blame to see the real author of a line use the ``-w`` flag so that whitespace changes are ignored.
Diffstat (limited to 'Source/Houdini')
-rw-r--r--Source/Houdini/AbstractHoudini.cs9428
-rw-r--r--Source/Houdini/AnnotationDependenceAnalyser.cs1756
-rw-r--r--Source/Houdini/Checker.cs912
-rw-r--r--Source/Houdini/ConcurrentHoudini.cs208
-rw-r--r--Source/Houdini/Houdini.cs2660
-rw-r--r--Source/Houdini/Houdini.csproj306
-rw-r--r--Source/Houdini/StagedHoudini.cs854
7 files changed, 8062 insertions, 8062 deletions
diff --git a/Source/Houdini/AbstractHoudini.cs b/Source/Houdini/AbstractHoudini.cs
index 522e8174..e3625dcc 100644
--- a/Source/Houdini/AbstractHoudini.cs
+++ b/Source/Houdini/AbstractHoudini.cs
@@ -1,4714 +1,4714 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Microsoft.Boogie;
-using Microsoft.Boogie.VCExprAST;
-using VC;
-using Outcome = VC.VCGen.Outcome;
-using Bpl = Microsoft.Boogie;
-using System.Diagnostics;
-using System.Diagnostics.Contracts;
-using Microsoft.Boogie.GraphUtil;
-
-namespace Microsoft.Boogie.Houdini {
-
- public class AbsHoudini
- {
- Dictionary<string, Function> existentialFunctions;
- Program program;
- Dictionary<string, Implementation> name2Impl;
- Dictionary<string, VCExpr> impl2VC;
- Dictionary<string, List<Tuple<string, Function, NAryExpr>>> impl2FuncCalls;
- // constant -> the naryexpr that it replaced
- Dictionary<string, NAryExpr> constant2FuncCall;
-
- // function -> its abstract value
- Dictionary<string, IAbstractDomain> function2Value;
-
- // impl -> functions assumed/asserted
- Dictionary<string, HashSet<string>> impl2functionsAsserted, impl2functionsAssumed;
-
- // funtions -> impls where assumed/asserted
- Dictionary<string, HashSet<string>> function2implAssumed, function2implAsserted;
-
- // impl -> handler, collector
- Dictionary<string, Tuple<ProverInterface.ErrorHandler, AbsHoudiniCounterexampleCollector>> impl2ErrorHandler;
-
- // Essentials: VCGen, Prover
- VCGen vcgen;
- ProverInterface prover;
-
- // Stats
- TimeSpan proverTime;
- int numProverQueries;
-
- public AbsHoudini(Program program, IAbstractDomain defaultElem)
- {
- this.program = program;
- this.impl2VC = new Dictionary<string, VCExpr>();
- this.impl2FuncCalls = new Dictionary<string, List<Tuple<string, Function, NAryExpr>>>();
- this.existentialFunctions = new Dictionary<string, Function>();
- this.name2Impl = new Dictionary<string, Implementation>();
- this.impl2functionsAsserted = new Dictionary<string, HashSet<string>>();
- this.impl2functionsAssumed = new Dictionary<string, HashSet<string>>();
- this.function2implAsserted = new Dictionary<string, HashSet<string>>();
- this.function2implAssumed = new Dictionary<string, HashSet<string>>();
- this.impl2ErrorHandler = new Dictionary<string, Tuple<ProverInterface.ErrorHandler, AbsHoudiniCounterexampleCollector>>();
- this.constant2FuncCall = new Dictionary<string, NAryExpr>();
-
- // Find the existential functions
- foreach (var func in program.Functions
- .Where(f => QKeyValue.FindBoolAttribute(f.Attributes, "existential")))
- existentialFunctions.Add(func.Name, func);
-
- this.function2Value = new Dictionary<string, IAbstractDomain>();
- foreach (var func in existentialFunctions.Values)
- {
- // Find if the function wishes to use a specific abstract domain
- var domain = QKeyValue.FindStringAttribute(func.Attributes, "absdomain");
- if (domain == null)
- {
- function2Value[func.Name] = defaultElem.Bottom();
- }
- else
- {
- function2Value[func.Name] = AbstractDomainFactory.GetInstance(domain);
- }
- }
- existentialFunctions.Keys.Iter(f => function2implAssumed.Add(f, new HashSet<string>()));
- existentialFunctions.Keys.Iter(f => function2implAsserted.Add(f, new HashSet<string>()));
-
- // type check
- existentialFunctions.Values.Iter(func =>
- {
- if (func.OutParams.Count != 1 || !func.OutParams[0].TypedIdent.Type.IsBool)
- throw new AbsHoudiniInternalError(string.Format("Existential function {0} must return bool", func.Name));
- if(func.Body != null)
- throw new AbsHoudiniInternalError(string.Format("Existential function {0} should not have a body", func.Name));
- var args = new List<Type>();
- func.InParams.Iter(v => args.Add(v.TypedIdent.Type));
- string msg = "";
- if (!function2Value[func.Name].TypeCheck(args, out msg))
- throw new AbsHoudiniInternalError("TypeError: " + msg);
- });
-
- //if (CommandLineOptions.Clo.ProverKillTime > 0)
- // CommandLineOptions.Clo.ProverOptions.Add(string.Format("TIME_LIMIT={0}", CommandLineOptions.Clo.ProverKillTime));
-
- Inline();
-
- this.vcgen = new VCGen(program, CommandLineOptions.Clo.SimplifyLogFilePath, CommandLineOptions.Clo.SimplifyLogFileAppend, new List<Checker>());
- this.prover = ProverInterface.CreateProver(program, CommandLineOptions.Clo.SimplifyLogFilePath, CommandLineOptions.Clo.SimplifyLogFileAppend, CommandLineOptions.Clo.ProverKillTime);
-
- this.proverTime = TimeSpan.Zero;
- this.numProverQueries = 0;
-
- program.Implementations
- .Where(impl => !impl.SkipVerification)
- .Iter(impl => name2Impl.Add(impl.Name, impl));
-
- // Let's do VC Gen (and also build dependencies)
- name2Impl.Values.Iter(GenVC);
- }
-
- public VCGenOutcome ComputeSummaries()
- {
- var overallOutcome = new VCGenOutcome(ProverInterface.Outcome.Valid, new List<Counterexample>());
-
- // Compute SCCs and determine a priority order for impls
- var Succ = new Dictionary<string, HashSet<string>>();
- var Pred = new Dictionary<string, HashSet<string>>();
- name2Impl.Keys.Iter(s => Succ[s] = new HashSet<string>());
- name2Impl.Keys.Iter(s => Pred[s] = new HashSet<string>());
-
- foreach(var impl in name2Impl.Keys) {
- Succ[impl] = new HashSet<string>();
- impl2functionsAsserted[impl].Iter(f =>
- function2implAssumed[f].Iter(succ =>
- {
- Succ[impl].Add(succ);
- Pred[succ].Add(impl);
- }));
- }
-
- var sccs = new StronglyConnectedComponents<string>(name2Impl.Keys,
- new Adjacency<string>(n => Pred[n]),
- new Adjacency<string>(n => Succ[n]));
- sccs.Compute();
-
- // impl -> priority
- var impl2Priority = new Dictionary<string, int>();
- int p = 0;
- foreach (var scc in sccs)
- {
- foreach (var impl in scc)
- {
- impl2Priority.Add(impl, p);
- p++;
- }
- }
-
- var worklist = new SortedSet<Tuple<int, string>>();
- name2Impl.Keys.Iter(k => worklist.Add(Tuple.Create(impl2Priority[k], k)));
-
- while (worklist.Any())
- {
- var impl = worklist.First().Item2;
- worklist.Remove(worklist.First());
-
- var gen = prover.VCExprGen;
- var terms = new List<Expr>();
- foreach (var tup in impl2FuncCalls[impl])
- {
- var controlVar = tup.Item2;
- var exprVars = tup.Item3;
- var varList = new List<Expr>();
- exprVars.Args.OfType<Expr>().Iter(v => varList.Add(v));
-
- var args = new List<Expr>();
- controlVar.InParams.Iter(v => args.Add(Expr.Ident(v)));
- Expr term = Expr.Eq(new NAryExpr(Token.NoToken, new FunctionCall(controlVar), args),
- function2Value[tup.Item1].Gamma(varList));
-
- if (controlVar.InParams.Count != 0)
- {
- term = new ForallExpr(Token.NoToken, new List<Variable>(controlVar.InParams.ToArray()),
- new Trigger(Token.NoToken, true, new List<Expr> { new NAryExpr(Token.NoToken, new FunctionCall(controlVar), args) }),
- term);
- }
- terms.Add(term);
- }
- var env = Expr.BinaryTreeAnd(terms);
-
- env.Typecheck(new TypecheckingContext((IErrorSink)null));
- var envVC = prover.Context.BoogieExprTranslator.Translate(env);
-
- var vc = gen.Implies(envVC, impl2VC[impl]);
-
- if (CommandLineOptions.Clo.Trace)
- {
- Console.WriteLine("Verifying {0}: ", impl);
- //Console.WriteLine("env: {0}", envVC);
- var envFuncs = new HashSet<string>();
- impl2FuncCalls[impl].Iter(tup => envFuncs.Add(tup.Item1));
- envFuncs.Iter(f => PrintFunction(existentialFunctions[f]));
- }
-
- var handler = impl2ErrorHandler[impl].Item1;
- var collector = impl2ErrorHandler[impl].Item2;
- collector.Reset(impl);
-
- var start = DateTime.Now;
-
- prover.Push();
- prover.Assert(gen.Not(vc), true);
- prover.FlushAxiomsToTheoremProver();
- prover.Check();
- ProverInterface.Outcome proverOutcome = prover.CheckOutcomeCore(handler);
-
- //prover.BeginCheck(impl, vc, handler);
- //ProverInterface.Outcome proverOutcome = prover.CheckOutcomeCore(handler);
-
- var inc = (DateTime.Now - start);
- proverTime += inc;
- numProverQueries++;
-
- if (CommandLineOptions.Clo.Trace)
- Console.WriteLine("Time taken = " + inc.TotalSeconds.ToString());
-
- if (proverOutcome == ProverInterface.Outcome.TimeOut || proverOutcome == ProverInterface.Outcome.OutOfMemory)
- {
- // pick some function; make it true and keep going
- bool changed = false;
- foreach (var f in impl2functionsAsserted[impl])
- {
- function2Value[f] = function2Value[f].MakeTop(out changed);
- if (changed) break;
- }
- if(!changed)
- return new VCGenOutcome(proverOutcome, new List<Counterexample>());
- }
-
- if (CommandLineOptions.Clo.Trace)
- Console.WriteLine(collector.numErrors > 0 ? "SAT" : "UNSAT");
-
- if (collector.numErrors > 0)
- {
- var funcsChanged = collector.funcsChanged;
- if (funcsChanged.Count == 0)
- {
- overallOutcome = new VCGenOutcome(ProverInterface.Outcome.Invalid, collector.errors);
- break;
- }
-
- // propagate dependent guys back into the worklist, including self
- var deps = new HashSet<string>();
- deps.Add(impl);
- funcsChanged.Iter(f => deps.UnionWith(function2implAssumed[f]));
-
- deps.Iter(s => worklist.Add(Tuple.Create(impl2Priority[s], s)));
- }
-
- prover.Pop();
- }
-
- if (CommandLineOptions.Clo.Trace)
- {
- Console.WriteLine("Prover time = {0}", proverTime.TotalSeconds.ToString("F2"));
- Console.WriteLine("Number of prover queries = " + numProverQueries);
- }
-
- if (CommandLineOptions.Clo.PrintAssignment)
- {
- // Print the answer
- existentialFunctions.Values.Iter(PrintFunction);
- }
-
- return overallOutcome;
- }
-
-
- public IEnumerable<Function> GetAssignment()
- {
- var ret = new List<Function>();
- foreach (var func in existentialFunctions.Values)
- {
- var invars = new List<Expr>(func.InParams.OfType<Variable>().Select(v => Expr.Ident(v)));
- func.Body = function2Value[func.Name].Gamma(invars);
- ret.Add(func);
- }
- return ret;
- }
-
- private void PrintFunction(Function function)
- {
- var tt = new TokenTextWriter(Console.Out, /*pretty=*/ false);
- var invars = new List<Expr>(function.InParams.OfType<Variable>().Select(v => Expr.Ident(v)));
- function.Body = function2Value[function.Name].Gamma(invars);
- function.Emit(tt, 0);
- tt.Close();
- }
-
- public HashSet<string> HandleCounterExample(string impl, Counterexample error)
- {
- var funcsChanged = new HashSet<string>();
- // Find the failing assert -- need to do a join there
- // return the set of functions whose definition has changed
- var cex = ExtractState(impl, error);
- foreach (var tup in cex)
- {
- function2Value[tup.Item1] = function2Value[tup.Item1].Join(tup.Item2);
- funcsChanged.Add(tup.Item1);
- }
- return funcsChanged;
- }
-
- private List<Tuple<string, List<Model.Element>>> ExtractState(string impl, Counterexample error)
- {
- var lastBlock = error.Trace.Last() as Block;
- AssertCmd failingAssert = null;
-
- CallCounterexample callCounterexample = error as CallCounterexample;
- if (callCounterexample != null)
- {
- Procedure failingProcedure = callCounterexample.FailingCall.Proc;
- Requires failingRequires = callCounterexample.FailingRequires;
- failingAssert = lastBlock.Cmds.OfType<AssertRequiresCmd>().FirstOrDefault(ac => ac.Requires == failingRequires);
- }
- ReturnCounterexample returnCounterexample = error as ReturnCounterexample;
- if (returnCounterexample != null)
- {
- Ensures failingEnsures = returnCounterexample.FailingEnsures;
- failingAssert = lastBlock.Cmds.OfType<AssertEnsuresCmd>().FirstOrDefault(ac => ac.Ensures == failingEnsures);
- }
- AssertCounterexample assertCounterexample = error as AssertCounterexample;
- if (assertCounterexample != null)
- {
- failingAssert = lastBlock.Cmds.OfType<AssertCmd>().FirstOrDefault(ac => ac == assertCounterexample.FailingAssert);
- }
-
- Debug.Assert(failingAssert != null);
- return ExtractState(impl, failingAssert.Expr, error.Model);
- }
-
- private static int existentialConstCounter = 0;
-
- private List<Tuple<string, List<Model.Element>>> ExtractState(string impl, Expr expr, Model model)
- {
- var funcsUsed = FunctionCollector.Collect(expr);
-
- var ret = new List<Tuple<string, List<Model.Element>>>();
-
- foreach (var tup in funcsUsed.Where(t => t.Item2 == null))
- {
- var constant = tup.Item1;
- if (!constant2FuncCall.ContainsKey(constant.Name))
- continue;
-
- var func = constant2FuncCall[constant.Name];
- var funcName = (func.Fun as FunctionCall).FunctionName;
- var vals = new List<Model.Element>();
- prover.Context.BoogieExprTranslator.Translate(func.Args).Iter(ve => vals.Add(getValue(ve, model)));
- ret.Add(Tuple.Create(funcName, vals));
- }
-
- foreach (var tup in funcsUsed.Where(t => t.Item2 != null))
- {
- var constant = tup.Item1;
- var boundExpr = tup.Item2;
-
- if (!constant2FuncCall.ContainsKey(constant.Name))
- continue;
-
- // There are some bound variables (because the existential function was inside an \exists).
- // We must find an assignment for bound varibles
-
- // First, peice apart the existential functions
- var cd = new Duplicator();
- var tup2 = ExistentialExprModelMassage.Massage(cd.VisitExpr(boundExpr.Body));
- var be = tup2.Item1;
- Expr env = Expr.True;
- foreach (var ahFunc in tup2.Item2)
- {
- var tup3 = impl2FuncCalls[impl].First(t => t.Item2.Name == ahFunc.Name);
- var varList = new List<Expr>();
- tup3.Item3.Args.OfType<Expr>().Iter(v => varList.Add(v));
-
- env = Expr.And(env, function2Value[tup3.Item1].Gamma(varList));
- }
- be = Expr.And(be, Expr.Not(env));
-
- // map formals to constants
- var formalToConstant = new Dictionary<string, Constant>();
- foreach (var f in boundExpr.Dummies.OfType<Variable>())
- formalToConstant.Add(f.Name, new Constant(Token.NoToken, new TypedIdent(Token.NoToken, f.Name + "@subst@" + (existentialConstCounter++), f.TypedIdent.Type), false));
- be = Substituter.Apply(new Substitution(v => formalToConstant.ContainsKey(v.Name) ? Expr.Ident(formalToConstant[v.Name]) : Expr.Ident(v)), be);
- formalToConstant.Values.Iter(v => prover.Context.DeclareConstant(v, false, null));
-
- var reporter = new AbstractHoudiniErrorReporter();
- var ve = prover.Context.BoogieExprTranslator.Translate(be);
- prover.Assert(ve, true);
- prover.Check();
- var proverOutcome = prover.CheckOutcomeCore(reporter);
- if (proverOutcome != ProverInterface.Outcome.Invalid)
- continue;
- model = reporter.model;
-
- var func = constant2FuncCall[constant.Name];
- var funcName = (func.Fun as FunctionCall).FunctionName;
- var vals = new List<Model.Element>();
- foreach (var funcArg in func.Args.OfType<Expr>())
- {
- var arg = Substituter.Apply(new Substitution(v => formalToConstant.ContainsKey(v.Name) ? Expr.Ident(formalToConstant[v.Name]) : Expr.Ident(v)), funcArg);
- vals.Add(getValue(prover.Context.BoogieExprTranslator.Translate(arg), model));
- }
- ret.Add(Tuple.Create(funcName, vals));
-
- }
-
- return ret;
- }
-
- private Model.Element getValue(VCExpr arg, Model model)
- {
-
-
- if (arg is VCExprLiteral)
- {
- //return model.GetElement(arg.ToString());
- return model.MkElement(arg.ToString());
- }
-
- else if (arg is VCExprVar)
- {
- var el = model.TryGetFunc(prover.Context.Lookup(arg as VCExprVar));
- if (el != null)
- {
- Debug.Assert(el.Arity == 0 && el.AppCount == 1);
- return el.Apps.First().Result;
- }
- else
- {
- // Variable not defined; assign arbitrary value
- if (arg.Type.IsBool)
- return model.MkElement("false");
- else if (arg.Type.IsInt)
- return model.MkIntElement(0);
- else
- return null;
- }
- }
- else if (arg is VCExprNAry && (arg as VCExprNAry).Op is VCExprBvOp)
- {
- // support for BV constants
- var bvc = (arg as VCExprNAry)[0] as VCExprLiteral;
- if (bvc != null)
- {
- var ret = model.TryMkElement(bvc.ToString() + arg.Type.ToString());
- if (ret != null && (ret is Model.BitVector)) return ret;
- }
- }
-
- object val;
-
- try
- {
- val = prover.Evaluate(arg);
- }
- catch (ProverInterface.VCExprEvaluationException)
- {
- Console.WriteLine("AbsHoudni: Error evaluating expression {0}", arg);
- throw;
- }
-
- if (val is int || val is bool || val is Microsoft.Basetypes.BigNum)
- {
- return model.MkElement(val.ToString());
- }
- else
- {
- Debug.Assert(false);
- }
- return null;
- }
-
- // Remove functions AbsHoudiniConstant from the expressions and substitute them with "true"
- class ExistentialExprModelMassage : StandardVisitor
- {
- List<Function> ahFuncs;
-
- public ExistentialExprModelMassage()
- {
- ahFuncs = new List<Function>();
- }
-
- public static Tuple<Expr, List<Function>> Massage(Expr expr)
- {
- var ee = new ExistentialExprModelMassage();
- expr = ee.VisitExpr(expr);
- return Tuple.Create(expr, ee.ahFuncs);
- }
-
- public override Expr VisitNAryExpr(NAryExpr node)
- {
- if (node.Fun is FunctionCall && (node.Fun as FunctionCall).FunctionName.StartsWith("AbsHoudiniConstant"))
- {
- ahFuncs.Add((node.Fun as FunctionCall).Func);
- return Expr.True;
- }
-
- return base.VisitNAryExpr(node);
- }
- }
-
- class FunctionCollector : ReadOnlyVisitor
- {
- public List<Tuple<Function, ExistsExpr>> functionsUsed;
- ExistsExpr existentialExpr;
-
- public FunctionCollector()
- {
- functionsUsed = new List<Tuple<Function, ExistsExpr>>();
- existentialExpr = null;
- }
-
- public static List<Tuple<Function, ExistsExpr>> Collect(Expr expr)
- {
- var fv = new FunctionCollector();
- fv.VisitExpr(expr);
- return fv.functionsUsed;
- }
-
- public override QuantifierExpr VisitQuantifierExpr(QuantifierExpr node)
- {
- var oldE = existentialExpr;
-
- if (node is ExistsExpr)
- existentialExpr = (node as ExistsExpr);
-
- node = base.VisitQuantifierExpr(node);
-
- existentialExpr = oldE;
- return node;
- }
-
- public override Expr VisitNAryExpr(NAryExpr node)
- {
- if (node.Fun is FunctionCall)
- {
- var collector = new VariableCollector();
- collector.Visit(node);
-
- if(existentialExpr != null && existentialExpr.Dummies.Intersect(collector.usedVars).Any())
- functionsUsed.Add(Tuple.Create((node.Fun as FunctionCall).Func, existentialExpr));
- else
- functionsUsed.Add(Tuple.Create<Function, ExistsExpr>((node.Fun as FunctionCall).Func, null));
- }
-
- return base.VisitNAryExpr(node);
- }
- }
-
- class AbsHoudiniCounterexampleCollector : VerifierCallback
- {
- public HashSet<string> funcsChanged;
- public string currImpl;
- public int numErrors;
- public List<Counterexample> errors;
-
- AbsHoudini container;
-
- public AbsHoudiniCounterexampleCollector(AbsHoudini container)
- {
- this.container = container;
- Reset(null);
- }
-
- public void Reset(string impl)
- {
- funcsChanged = new HashSet<string>();
- currImpl = impl;
- numErrors = 0;
- errors = new List<Counterexample>();
- }
-
- public override void OnCounterexample(Counterexample ce, string reason)
- {
- numErrors++;
- errors.Add(ce);
-
- funcsChanged.UnionWith(
- container.HandleCounterExample(currImpl, ce));
- }
- }
-
- private void GenVC(Implementation impl)
- {
- ModelViewInfo mvInfo;
- Dictionary<int, Absy> label2absy;
- var collector = new AbsHoudiniCounterexampleCollector(this);
- collector.OnProgress("HdnVCGen", 0, 0, 0.0);
-
- if (CommandLineOptions.Clo.Trace)
- {
- Console.WriteLine("Generating VC of {0}", impl.Name);
- }
-
- vcgen.ConvertCFG2DAG(impl);
- var gotoCmdOrigins = vcgen.PassifyImpl(impl, out mvInfo);
-
- // Inline functions
- (new InlineFunctionCalls()).VisitBlockList(impl.Blocks);
-
-
- StripOutermostForall(impl);
- //ExtractQuantifiedExprs(impl);
- ExtractBoolExprs(impl);
-
- //CommandLineOptions.Clo.PrintInstrumented = true;
- //using (var tt = new TokenTextWriter(Console.Out))
- // impl.Emit(tt, 0);
-
- // Intercept the FunctionCalls of the existential functions, and replace them with Boolean constants
- var existentialFunctionNames = new HashSet<string>(existentialFunctions.Keys);
- var fv = new ReplaceFunctionCalls(existentialFunctionNames);
- fv.VisitBlockList(impl.Blocks);
-
- //using (var tt = new TokenTextWriter(Console.Out))
- // impl.Emit(tt, 0);
-
-
- impl2functionsAsserted.Add(impl.Name, fv.functionsAsserted);
- impl2functionsAssumed.Add(impl.Name, fv.functionsAssumed);
-
- fv.functionsAssumed.Iter(f => function2implAssumed[f].Add(impl.Name));
- fv.functionsAsserted.Iter(f => function2implAsserted[f].Add(impl.Name));
-
- impl2FuncCalls.Add(impl.Name, fv.functionsUsed);
- fv.functionsUsed.Iter(tup => constant2FuncCall.Add(tup.Item2.Name, tup.Item3));
-
- var gen = prover.VCExprGen;
- VCExpr controlFlowVariableExpr = CommandLineOptions.Clo.UseLabels ? null : gen.Integer(Microsoft.Basetypes.BigNum.ZERO);
-
- var vcexpr = vcgen.GenerateVC(impl, controlFlowVariableExpr, out label2absy, prover.Context);
- if (!CommandLineOptions.Clo.UseLabels)
- {
- VCExpr controlFlowFunctionAppl = gen.ControlFlowFunctionApplication(gen.Integer(Microsoft.Basetypes.BigNum.ZERO), gen.Integer(Microsoft.Basetypes.BigNum.ZERO));
- VCExpr eqExpr = gen.Eq(controlFlowFunctionAppl, gen.Integer(Microsoft.Basetypes.BigNum.FromInt(impl.Blocks[0].UniqueId)));
- vcexpr = gen.Implies(eqExpr, vcexpr);
- }
-
- ProverInterface.ErrorHandler handler = null;
- if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Local)
- handler = new VCGen.ErrorReporterLocal(gotoCmdOrigins, label2absy, impl.Blocks, vcgen.incarnationOriginMap, collector, mvInfo, prover.Context, program);
- else
- handler = new VCGen.ErrorReporter(gotoCmdOrigins, label2absy, impl.Blocks, vcgen.incarnationOriginMap, collector, mvInfo, prover.Context, program);
-
- impl2ErrorHandler.Add(impl.Name, Tuple.Create(handler, collector));
-
- //Console.WriteLine("VC of {0}: {1}", impl.Name, vcexpr);
-
- // Create a macro so that the VC can sit with the theorem prover
- Macro macro = new Macro(Token.NoToken, impl.Name + "Macro", new List<Variable>(), new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", Bpl.Type.Bool), false));
- prover.DefineMacro(macro, vcexpr);
-
- // Store VC
- impl2VC.Add(impl.Name, gen.Function(macro));
-
- // HACK: push the definitions of constants involved in function calls
- // It is possible that some constants only appear in function calls. Thus, when
- // they are replaced by Boolean constants, it is possible that (get-value) will
- // fail if the expression involves such constants. All we need to do is make sure
- // these constants are declared, because otherwise, semantically we are doing
- // the right thing.
- foreach (var tup in fv.functionsUsed)
- {
- // Ignore ones with bound varibles
- if (tup.Item2.InParams.Count > 0) continue;
- var tt = prover.Context.BoogieExprTranslator.Translate(tup.Item3);
- tt = prover.VCExprGen.Or(VCExpressionGenerator.True, tt);
- prover.Assert(tt, true);
- }
- }
-
- // convert "foo(... forall e ...) to:
- // (p iff forall e) ==> foo(... p ...)
- // where p is a fresh boolean variable and foo is an existential constant
- private void ExtractQuantifiedExprs(Implementation impl)
- {
- var funcs = new HashSet<string>(existentialFunctions.Keys);
- foreach (var blk in impl.Blocks)
- {
- foreach (var acmd in blk.Cmds.OfType<AssertCmd>())
- {
- var ret = ExtractQuantifiers.Extract(acmd.Expr, funcs);
- acmd.Expr = ret.Item1;
- impl.LocVars.AddRange(ret.Item2);
- }
- }
- }
-
- // convert "foo(... e ...) to:
- // (p iff e) ==> foo(... p ...)
- // where p is a fresh boolean variable, foo is an existential constant
- // and e is a Boolean-typed argument of foo
- private void ExtractBoolExprs(Implementation impl)
- {
- var funcs = new HashSet<string>(existentialFunctions.Keys);
- foreach (var blk in impl.Blocks)
- {
- foreach (var acmd in blk.Cmds.OfType<AssertCmd>())
- {
- var ret = ExtractBoolArgs.Extract(acmd.Expr, funcs);
- acmd.Expr = ret.Item1;
- impl.LocVars.AddRange(ret.Item2);
- }
- }
- }
-
- // convert "assert e1 && forall x: e2" to
- // assert e1 && e2[x <- x@bound]
- private void StripOutermostForall(Implementation impl)
- {
- var funcs = new HashSet<string>(existentialFunctions.Keys);
- foreach (var blk in impl.Blocks)
- {
- foreach (var acmd in blk.Cmds.OfType<AssertCmd>())
- {
- var ret = StripQuantifiers.Run(acmd.Expr, funcs);
- acmd.Expr = ret.Item1;
- impl.LocVars.AddRange(ret.Item2);
- }
- }
- }
-
- private void Inline()
- {
- if (CommandLineOptions.Clo.InlineDepth < 0)
- return;
-
- var callGraph = BuildCallGraph();
-
- foreach (Implementation impl in callGraph.Nodes)
- {
- InlineEnsuresVisitor inlineEnsuresVisitor = new InlineEnsuresVisitor();
- inlineEnsuresVisitor.Visit(impl);
- }
-
- foreach (Implementation impl in callGraph.Nodes)
- {
- impl.OriginalBlocks = impl.Blocks;
- impl.OriginalLocVars = impl.LocVars;
- }
- foreach (Implementation impl in callGraph.Nodes)
- {
- CommandLineOptions.Inlining savedOption = CommandLineOptions.Clo.ProcedureInlining;
- CommandLineOptions.Clo.ProcedureInlining = CommandLineOptions.Inlining.Spec;
- Inliner.ProcessImplementationForHoudini(program, impl);
- CommandLineOptions.Clo.ProcedureInlining = savedOption;
- }
- foreach (Implementation impl in callGraph.Nodes)
- {
- impl.OriginalBlocks = null;
- impl.OriginalLocVars = null;
- }
-
- Graph<Implementation> oldCallGraph = callGraph;
- callGraph = new Graph<Implementation>();
- foreach (Implementation impl in oldCallGraph.Nodes)
- {
- callGraph.AddSource(impl);
- }
- foreach (Tuple<Implementation, Implementation> edge in oldCallGraph.Edges)
- {
- callGraph.AddEdge(edge.Item1, edge.Item2);
- }
- int count = CommandLineOptions.Clo.InlineDepth;
- while (count > 0)
- {
- foreach (Implementation impl in oldCallGraph.Nodes)
- {
- List<Implementation> newNodes = new List<Implementation>();
- foreach (Implementation succ in callGraph.Successors(impl))
- {
- newNodes.AddRange(oldCallGraph.Successors(succ));
- }
- foreach (Implementation newNode in newNodes)
- {
- callGraph.AddEdge(impl, newNode);
- }
- }
- count--;
- }
- }
-
- private Graph<Implementation> BuildCallGraph()
- {
- Graph<Implementation> callGraph = new Graph<Implementation>();
- Dictionary<Procedure, HashSet<Implementation>> procToImpls = new Dictionary<Procedure, HashSet<Implementation>>();
- foreach (var proc in program.Procedures)
- {
- procToImpls[proc] = new HashSet<Implementation>();
- }
- foreach (var impl in program.Implementations)
- {
- if (impl.SkipVerification) continue;
- callGraph.AddSource(impl);
- procToImpls[impl.Proc].Add(impl);
- }
- foreach (var impl in program.Implementations)
- {
- if (impl.SkipVerification) continue;
- foreach (Block b in impl.Blocks)
- {
- foreach (Cmd c in b.Cmds)
- {
- CallCmd cc = c as CallCmd;
- if (cc == null) continue;
- foreach (Implementation callee in procToImpls[cc.Proc])
- {
- callGraph.AddEdge(impl, callee);
- }
- }
- }
- }
- return callGraph;
- }
-
- }
-
- class InlineFunctionCalls : StandardVisitor
- {
- public Stack<string> inlinedFunctionsStack;
-
- public InlineFunctionCalls()
- {
- inlinedFunctionsStack = new Stack<string>();
- }
-
- public override Expr VisitNAryExpr(NAryExpr node)
- {
- var fc = node.Fun as FunctionCall;
- if (fc != null && fc.Func.Body != null && QKeyValue.FindBoolAttribute(fc.Func.Attributes, "inline"))
- {
- if (inlinedFunctionsStack.Contains(fc.Func.Name))
- {
- // recursion detected
- throw new AbsHoudiniInternalError("Recursion detected in function declarations");
- }
-
- // create a substitution
- var subst = new Dictionary<Variable, Expr>();
- for (int i = 0; i < node.Args.Count; i++)
- {
- subst.Add(fc.Func.InParams[i], node.Args[i]);
- }
-
- var e =
- Substituter.Apply(new Substitution(v => subst.ContainsKey(v) ? subst[v] : Expr.Ident(v)), fc.Func.Body);
-
- inlinedFunctionsStack.Push(fc.Func.Name);
-
- e = base.VisitExpr(e);
-
- inlinedFunctionsStack.Pop();
-
- return e;
- }
- return base.VisitNAryExpr(node);
- }
- }
-
- class ReplaceFunctionCalls : StandardVisitor
- {
- public List<Tuple<string, Function, NAryExpr>> functionsUsed;
- public List<Function> boolConstants;
-
- public HashSet<string> functionsAssumed;
- public HashSet<string> functionsAsserted;
- HashSet<string> functionsToReplace;
-
- private bool inAssume;
- private bool inAssert;
- private bool inFunction;
- private List<Dictionary<string, Variable>> boundVars;
- private static int IdCounter = 0;
-
- public ReplaceFunctionCalls(HashSet<string> functionsToReplace)
- {
- this.functionsUsed = new List<Tuple<string, Function, NAryExpr>>();
- this.functionsToReplace = functionsToReplace;
- this.functionsAsserted = new HashSet<string>();
- this.functionsAssumed = new HashSet<string>();
- this.boolConstants = new List<Function>();
- this.boundVars = new List<Dictionary<string, Variable>>();
-
- inAssume = false;
- inAssert = false;
- inFunction = false;
- }
-
- public override Cmd VisitAssertCmd(AssertCmd node)
- {
- inAssert = true;
- var ret = base.VisitAssertCmd(node);
- inAssert = false;
- return ret;
- }
-
- public override Cmd VisitAssertEnsuresCmd(AssertEnsuresCmd node)
- {
- return this.VisitAssertCmd(node);
- }
-
- public override Cmd VisitAssertRequiresCmd(AssertRequiresCmd node)
- {
- return this.VisitAssertCmd(node);
- }
-
- public override Cmd VisitAssumeCmd(AssumeCmd node)
- {
- inAssume = true;
- var ret = base.VisitAssumeCmd(node);
- inAssume = false;
- return ret;
- }
-
- public override QuantifierExpr VisitQuantifierExpr(QuantifierExpr node)
- {
- // gather the quantified variables
- var dummies = new Dictionary<string, Variable>();
- node.Dummies.Iter(v => dummies.Add(v.Name, v));
-
- boundVars.Add(dummies);
-
- node = base.VisitQuantifierExpr(node);
-
- boundVars.RemoveAt(boundVars.Count - 1);
-
- return node;
- }
-
- public override Expr VisitNAryExpr(NAryExpr node)
- {
- var inF = inFunction;
-
- if (node.Fun is FunctionCall && functionsToReplace.Contains((node.Fun as FunctionCall).FunctionName))
- {
- found((node.Fun as FunctionCall).FunctionName);
- inFunction = true;
-
- // collect all the variables used by this function
- var collector = new VariableCollector();
- collector.VisitExpr(node);
-
- // Find the outermost bound variables
- var bound = new List<Variable>();
- if(boundVars.Count > 0)
- bound.AddRange(collector.usedVars.Intersect(boundVars[0].Values));
-
- // create boolean function to replace this guy
- var constant = new Function(Token.NoToken, "AbsHoudiniConstant" + IdCounter, bound,
- new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "r", Microsoft.Boogie.Type.Bool), false));
- IdCounter++;
-
- functionsUsed.Add(Tuple.Create((node.Fun as FunctionCall).FunctionName, constant, node));
- boolConstants.Add(constant);
-
- var args = new List<Expr>();
- bound.OfType<Variable>().Select(v => Expr.Ident(v)).Iter(v => args.Add(v));
- return new NAryExpr(Token.NoToken, new FunctionCall(constant), args);
- }
- var ret = base.VisitNAryExpr(node);
-
- inFunction = inF;
-
- return ret;
- }
-
- public override Expr VisitIdentifierExpr(IdentifierExpr node)
- {
- if (inFunction)
- {
- // Inside functions we can only refer to the outermost bound variables
- for (int i = boundVars.Count - 1; i >= 1; i--)
- {
- if (boundVars[i].ContainsKey(node.Name))
- throw new AbsHoudiniInternalError("Existential functions can only refer to outermost bound variables in an expression");
- }
- }
-
- return base.VisitIdentifierExpr(node);
- }
-
- private void found(string func)
- {
- if (inAssume) functionsAssumed.Add(func);
- if (inAssert) functionsAsserted.Add(func);
- }
-
- }
-
- // convert "foo(... e ...) to:
- // (p iff e) ==> foo(... p ...)
- // where p is a fresh boolean variable, foo is an existential constant
- // and e is a Boolean-typed argument of foo
- class ExtractBoolArgs : StandardVisitor
- {
- static int freshConstCounter = 0;
- HashSet<string> existentialFunctions;
- HashSet<Constant> newConstants;
-
- private ExtractBoolArgs(HashSet<string> existentialFunctions)
- {
- this.existentialFunctions = existentialFunctions;
- this.newConstants = new HashSet<Constant>();
- }
-
- public static Tuple<Expr, IEnumerable<Constant>> Extract(Expr expr, HashSet<string> existentialFunctions)
- {
- var eq = new ExtractBoolArgs(existentialFunctions);
- expr = eq.VisitExpr(expr);
- return Tuple.Create(expr, eq.newConstants.AsEnumerable());
- }
-
- public override Expr VisitNAryExpr(NAryExpr node)
- {
- if (node.Fun is FunctionCall && existentialFunctions.Contains((node.Fun as FunctionCall).FunctionName))
- {
- var constants = new Dictionary<Constant, Expr>();
- for (int i = 0; i < node.Args.Count; i++)
- {
- if (node.Args[i].Type == Type.Bool)
- {
- var constant = new Constant(Token.NoToken, new TypedIdent(Token.NoToken,
- "boolArg@const" + freshConstCounter, Microsoft.Boogie.Type.Bool), false);
- freshConstCounter++;
- constants.Add(constant, node.Args[i]);
- node.Args[i] = Expr.Ident(constant);
- }
- }
-
- newConstants.UnionWith(constants.Keys);
-
- Expr ret = Expr.True;
- foreach (var tup in constants)
- {
- ret = Expr.And(ret, Expr.Eq(Expr.Ident(tup.Key), tup.Value));
- }
- return Expr.Imp(ret, node);
- }
-
- return base.VisitNAryExpr(node);
- }
- }
-
-
- // convert "foo(... forall e ...) to:
- // (p iff forall e) ==> foo(... p ...)
- // where p is a fresh boolean variable and foo is an existential constant
- class ExtractQuantifiers : StandardVisitor
- {
- static int freshConstCounter = 0;
- HashSet<string> existentialFunctions;
- bool insideExistential;
- Dictionary<Constant, Expr> newConstants;
-
- private ExtractQuantifiers(HashSet<string> existentialFunctions)
- {
- this.existentialFunctions = existentialFunctions;
- insideExistential = false;
- newConstants = new Dictionary<Constant, Expr>();
- }
-
- public static Tuple<Expr, IEnumerable<Constant>> Extract(Expr expr, HashSet<string> existentialFunctions)
- {
- var eq = new ExtractQuantifiers(existentialFunctions);
- expr = eq.VisitExpr(expr);
- Expr ret = Expr.True;
- foreach (var tup in eq.newConstants)
- {
- ret = Expr.And(ret, Expr.Eq(Expr.Ident(tup.Key), tup.Value));
- }
- ret = Expr.Imp(ret, expr);
- return Tuple.Create(ret, eq.newConstants.Keys.AsEnumerable());
- }
-
- public override Expr VisitNAryExpr(NAryExpr node)
- {
- var oldIE = insideExistential;
-
- if (node.Fun is FunctionCall && existentialFunctions.Contains((node.Fun as FunctionCall).FunctionName))
- insideExistential = true;
-
- var ret = base.VisitNAryExpr(node);
-
- insideExistential = oldIE;
- return ret;
- }
-
- public override Expr VisitExpr(Expr node)
- {
- if (node is QuantifierExpr)
- {
- return MyVisitQuantifierExpr(node as QuantifierExpr);
- }
- return base.VisitExpr(node);
- }
-
- public Expr MyVisitQuantifierExpr(QuantifierExpr node)
- {
- node = base.VisitQuantifierExpr(node);
-
- if (insideExistential)
- {
- var constant = new Constant(Token.NoToken, new TypedIdent(Token.NoToken,
- "quant@const" + freshConstCounter, Microsoft.Boogie.Type.Bool), false);
- freshConstCounter++;
-
- newConstants.Add(constant, node);
-
- return Expr.Ident(constant);
- }
-
- return node;
- }
- }
-
- // convert "assert e1 && forall x: e2" to
- // assert e1 && e2[x <- x@bound]
- // only if e2 has an existential function
- class StripQuantifiers : StandardVisitor
- {
- static int boundVarCounter = 0;
-
- // 0 -> None, 1 -> Forall, 2 -> Exists, 3 -> Nested
- int insideQuantifier;
-
- bool searchExistentialFunction;
- bool foundExistentialFunction;
-
- HashSet<string> existentialFunctions;
- Dictionary<string, LocalVariable> subst;
- List<LocalVariable> LocalsToAdd;
-
- private StripQuantifiers(HashSet<string> existentialFunctions)
- {
- this.existentialFunctions = existentialFunctions;
- insideQuantifier = 0;
- searchExistentialFunction = false;
- foundExistentialFunction = false;
- LocalsToAdd = new List<LocalVariable>();
- subst = null;
- }
-
- public static Tuple<Expr,List<LocalVariable>> Run(Expr expr, HashSet<string> existentialFunctions)
- {
- // check for type errors first
- var sq = new StripQuantifiers(existentialFunctions);
- var ret = sq.VisitExpr(expr);
-
- return Tuple.Create(ret, sq.LocalsToAdd);
- }
-
- public override Expr VisitExpr(Expr node)
- {
- if (node is QuantifierExpr)
- {
- return MyVisitQuantifierExpr(node as QuantifierExpr);
- }
-
- return base.VisitExpr(node);
- }
-
- private Expr MyVisitQuantifierExpr(QuantifierExpr node)
- {
- var oldIQ = insideQuantifier;
- Expr ret = node;
-
- // update "insideQuantifier"
- if (insideQuantifier == 0)
- {
- if (node is ForallExpr) insideQuantifier = 1;
- else insideQuantifier = 2;
- }
- else if (insideQuantifier > 0)
- {
- insideQuantifier = 3;
- }
-
- // Going inside Forall?
- if (insideQuantifier == 1)
- {
- // see if there is any existential function inside
- searchExistentialFunction = true;
- foundExistentialFunction = false;
- base.VisitQuantifierExpr(node);
-
- if (foundExistentialFunction)
- {
- // create substitution to apply
- subst = new Dictionary<string, LocalVariable>();
- foreach (var bv in node.Dummies.OfType<Variable>())
- {
- var lv = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken,
- bv + "@bound" + boundVarCounter, bv.TypedIdent.Type));
- boundVarCounter++;
- subst.Add(bv.Name, lv);
- LocalsToAdd.Add(lv);
- }
-
- // apply the subst
- var body = base.VisitExpr(node.Body);
- ret = body;
-
- subst = null;
- }
- else
- {
- ret = base.VisitQuantifierExpr(node);
- }
-
- searchExistentialFunction = false;
- foundExistentialFunction = false;
- }
- else
- {
- ret = base.VisitQuantifierExpr(node);
- }
-
- insideQuantifier = oldIQ;
- return ret;
- }
-
- public override Expr VisitNAryExpr(NAryExpr node)
- {
- if (node.Fun is FunctionCall && existentialFunctions.Contains((node.Fun as FunctionCall).FunctionName))
- {
- if (insideQuantifier == 3)
- throw new AbsHoudiniInternalError("Existential function found inside exists, or nested foralls");
-
- if (searchExistentialFunction)
- foundExistentialFunction = true;
-
- }
-
- return base.VisitNAryExpr(node);
- }
-
- public override Expr VisitIdentifierExpr(IdentifierExpr node)
- {
- if (subst != null && subst.ContainsKey(node.Name))
- return Expr.Ident(subst[node.Name]);
- return base.VisitIdentifierExpr(node);
- }
-
- }
-
- public class Intervals : IAbstractDomain
- {
- // [lower, upper]
- int upper;
- int lower;
- // or: \bot
- bool isBottom;
- // number of times join has been performed
- int nJoin;
- // number of times before we widen
- readonly static int maxJoin = 5;
-
- public Intervals()
- {
- this.upper = 0;
- this.lower = 0;
- this.nJoin = 0;
- this.isBottom = true;
- }
-
- private Intervals(int lower, int upper, int nJoin)
- {
- this.upper = upper;
- this.lower = lower;
- this.nJoin = nJoin;
- }
-
- public IAbstractDomain Bottom()
- {
- return new Intervals();
- }
-
- public IAbstractDomain MakeTop(out bool changed)
- {
- if (lower == Int32.MinValue && upper == Int32.MaxValue)
- {
- changed = false;
- return this;
- }
- changed = true;
- return new Intervals(Int32.MinValue, Int32.MaxValue, 0);
- }
-
- public IAbstractDomain Join(List<Model.Element> states)
- {
- Debug.Assert(states.Count == 1);
- var state = states[0] as Model.Integer;
- if (state == null)
- throw new AbsHoudiniInternalError("Incorrect type, expected int");
- var intval = state.AsInt();
-
- if (isBottom)
- {
- return new Intervals(intval, intval, 1);
- }
-
- if(intval >= lower && intval <= upper)
- return this;
-
- if (nJoin > maxJoin)
- {
- // widen
- if (intval > upper)
- return new Intervals(lower, Int32.MaxValue, 1);
- if(intval < lower)
- return new Intervals(Int32.MinValue, upper, 1);
-
- Debug.Assert(false);
- }
-
- if (intval > upper)
- return new Intervals(lower, intval, nJoin + 1);
- if (intval < lower)
- return new Intervals(intval, upper, nJoin + 1);
-
- Debug.Assert(false);
- return null;
- }
-
- public Expr Gamma(List<Expr> vars)
- {
- Debug.Assert(vars.Count == 1);
- var v = vars[0];
- if (isBottom) return Expr.False;
- Expr ret = Expr.True;
- if (lower != Int32.MinValue)
- ret = Expr.And(ret, Expr.Ge(v, Expr.Literal(lower)));
- if (upper != Int32.MaxValue)
- ret = Expr.And(ret, Expr.Le(v, Expr.Literal(upper)));
- return ret;
- }
-
- public bool TypeCheck(List<Type> argTypes, out string msg)
- {
- msg = "";
- if (argTypes.Count != 1)
- {
- msg = "Illegal number of arguments";
- return false;
- }
- if (!argTypes[0].IsInt)
- {
- msg = "Illegal type, expecting int";
- return false;
- }
- return true;
- }
- }
-
- public class PredicateAbsFullElem : PredicateAbsElem
- {
- public PredicateAbsFullElem()
- : base(1000)
- { }
- }
-
- public class PredicateAbsElem : IAbstractDomain
- {
- public static class ExprExt
- {
- public static Expr AndSimp(Expr e1, Expr e2)
- {
- if (e1 == Expr.True) return e2;
- if (e2 == Expr.True) return e1;
- if (e1 == Expr.False || e2 == Expr.False) return Expr.False;
- return Expr.And(e1, e2);
- }
-
- public static Expr OrSimp(Expr e1, Expr e2)
- {
- if (e1 == Expr.False) return e2;
- if (e2 == Expr.False) return e1;
- if (e1 == Expr.True || e2 == Expr.True) return Expr.True;
- return Expr.Or(e1, e2);
- }
- }
-
- class Disjunct
- {
- HashSet<int> pos;
- HashSet<int> neg;
- bool isTrue;
-
- public Disjunct()
- {
- isTrue = true;
- pos = new HashSet<int>();
- neg = new HashSet<int>();
- }
-
- public Disjunct(IEnumerable<int> pos, IEnumerable<int> neg, int bound)
- {
- this.isTrue = false;
- this.pos = new HashSet<int>(pos);
- this.neg = new HashSet<int>(neg);
- if (this.pos.Overlaps(this.neg))
- {
- this.isTrue = true;
- this.pos = new HashSet<int>();
- this.neg = new HashSet<int>();
- }
- if (this.pos.Count + this.neg.Count > bound)
- {
- // Set to true
- this.isTrue = true;
- this.pos = new HashSet<int>();
- this.neg = new HashSet<int>();
- }
-
- }
-
- public Disjunct OR(Disjunct that, int bound)
- {
- if (isTrue)
- return this;
- if (that.isTrue)
- return that;
-
- return new Disjunct(this.pos.Concat(that.pos), this.neg.Concat(that.neg), bound);
- }
-
- public bool Implies(Disjunct that)
- {
- if (isTrue) return that.isTrue;
- if (that.isTrue) return true;
-
- return pos.IsSubsetOf(that.pos) && neg.IsSubsetOf(that.neg);
- }
-
- public Expr Gamma(List<Expr> vars)
- {
- if (isTrue) return Expr.True;
- Expr ret = Expr.False;
- pos.Iter(i => ret = ExprExt.OrSimp(ret, vars[i]));
- neg.Iter(i => ret = ExprExt.OrSimp(ret, Expr.Not(vars[i])));
- return ret;
- }
- }
-
- // Conjunction of Disjuncts
- List<Disjunct> conjuncts;
- int DisjunctBound;
- bool isFalse;
-
- public PredicateAbsElem()
- {
- this.conjuncts = new List<Disjunct>();
- this.isFalse = true;
- this.DisjunctBound = 3;
- }
-
- public PredicateAbsElem(int bound)
- {
- this.conjuncts = new List<Disjunct>();
- this.isFalse = true;
- this.DisjunctBound = bound;
- }
-
- public IAbstractDomain Bottom()
- {
- return new PredicateAbsElem(DisjunctBound);
- }
-
- public IAbstractDomain MakeTop(out bool changed)
- {
- if (conjuncts.Count == 0)
- {
- changed = false;
- return this;
- }
- changed = true;
- var ret = new PredicateAbsElem(DisjunctBound);
- ret.isFalse = false;
- return ret;
- }
-
- public IAbstractDomain Join(List<Model.Element> state)
- {
- if (state.Any(me => !(me is Model.Boolean)))
- throw new AbsHoudiniInternalError("Predicate Abstraction requires that each argument be of type bool");
-
- // quick return if this == true
- if (!this.isFalse && conjuncts.Count == 0)
- return this;
-
- var ret = new PredicateAbsElem(DisjunctBound);
- ret.isFalse = false;
-
- for (int i = 0; i < state.Count; i++)
- {
- var b = (state[i] as Model.Boolean).Value;
- Disjunct d = null;
- if (b) d = new Disjunct(new int[] { i }, new int[] { }, DisjunctBound);
- else d = new Disjunct(new int[] { }, new int[] { i }, DisjunctBound);
-
- if (isFalse)
- ret.AddDisjunct(d);
- else
- {
- conjuncts.Iter(c => ret.AddDisjunct(c.OR(d, DisjunctBound)));
- }
- }
-
- return ret;
-
- }
-
- public Expr Gamma(List<Expr> vars)
- {
- if (isFalse) return Expr.False;
- Expr ret = Expr.True;
-
- foreach (var c in conjuncts)
- {
- ret = ExprExt.AndSimp(ret, c.Gamma(vars));
- }
-
- return ret;
- }
-
- public bool TypeCheck(List<Type> argTypes, out string msg)
- {
- msg = "";
- if (argTypes.Any(t => !t.IsBool))
- {
- msg = "Illegal type, expecting bool";
- return false;
- }
- return true;
- }
-
- private void AddDisjunct(Disjunct d)
- {
- if (conjuncts.Any(c => c.Implies(d)))
- return;
-
- conjuncts.RemoveAll(c => d.Implies(c));
- conjuncts.Add(d);
- }
- }
-
- // [false -- (x == true) -- true]
- public class HoudiniConst : IAbstractDomain
- {
- bool isBottom;
- bool isTop;
-
- private HoudiniConst(bool isTop, bool isBottom)
- {
- this.isBottom = isBottom;
- this.isTop = isTop;
- Debug.Assert(!(isTop && isBottom));
- }
-
- public static HoudiniConst GetExtObj()
- {
- return new HoudiniConst(false, false);
- }
-
- public static HoudiniConst GetTop()
- {
- return new HoudiniConst(true, false);
- }
-
- public static HoudiniConst GetBottom()
- {
- return new HoudiniConst(false, true);
- }
-
- public IAbstractDomain Bottom()
- {
- return GetBottom();
- }
-
- public IAbstractDomain MakeTop(out bool changed)
- {
- changed = false;
- if (isTop) return this;
- changed = true;
- return GetTop();
- }
-
- public IAbstractDomain Join(List<Model.Element> states)
- {
- Debug.Assert(states.Count == 1);
- var state = states[0];
-
- if (isTop) return this;
-
- if (state is Model.Boolean)
- {
- if ((state as Model.Boolean).Value)
- return GetExtObj();
- }
-
- return GetTop();
- }
-
- public Expr Gamma(List<Expr> vars)
- {
- Debug.Assert(vars.Count == 1);
- var v = vars[0];
- if (isBottom) return Expr.False;
- if (isTop) return Expr.True;
- return v;
- }
-
- public bool TypeCheck(List<Type> argTypes, out string msg)
- {
- msg = "";
- if (argTypes.Count != 1)
- {
- msg = "Illegal number of arguments, expecting 1";
- return false;
- }
- if (!argTypes[0].IsBool)
- {
- msg = "Illegal type, expecting bool";
- return false;
- }
- return true;
- }
- }
-
- // foo(x) = x < 2^j for some j <= 16
- public class PowDomain : IAbstractDomain
- {
- enum Val { FALSE, NEITHER, TRUE };
- Val tlevel;
- bool isBottom { get { return tlevel == Val.FALSE; } }
- bool isTop { get { return tlevel == Val.TRUE; } }
-
- readonly int Max = 16;
-
- int upper; // <= Max
-
- private PowDomain(Val tlevel) :
- this(tlevel, 0) { }
-
- private PowDomain(Val tlevel, int upper)
- {
- this.tlevel = tlevel;
- this.upper = upper;
- }
-
- public static IAbstractDomain GetBottom()
- {
- return new PowDomain(Val.FALSE) as IAbstractDomain;
- }
-
- public IAbstractDomain MakeTop(out bool changed)
- {
- if (isTop)
- {
- changed = false;
- return this;
- }
- changed = true;
- return new PowDomain(Val.TRUE);
- }
-
- IAbstractDomain IAbstractDomain.Bottom()
- {
- return GetBottom();
- }
-
- IAbstractDomain IAbstractDomain.Join(List<Model.Element> state)
- {
- if (isTop) return this;
-
- int v = 0;
- if (state[0] is Model.BitVector)
- v = (state[0] as Model.BitVector).AsInt();
- else if (state[0] is Model.Integer)
- v = (state[0] as Model.Integer).AsInt();
- else Debug.Assert(false);
-
- var nupper = upper;
- while ((1 << nupper) < v) nupper++;
- var ntlevel = Val.NEITHER;
- if (nupper > Max) ntlevel = Val.TRUE;
- return new PowDomain(ntlevel, nupper);
- }
-
- Expr IAbstractDomain.Gamma(List<Expr> vars)
- {
- if (isBottom) return Expr.False;
- if (isTop) return Expr.True;
- var v = vars[0];
- if (v.Type.IsBv)
- {
- var bits = v.Type.BvBits;
- if (!AbstractDomainFactory.bvslt.ContainsKey(bits))
- throw new AbsHoudiniInternalError("No builtin function found for bv" + bits.ToString());
- var bvslt = AbstractDomainFactory.bvslt[bits];
- return new NAryExpr(Token.NoToken, new FunctionCall(bvslt), new List<Expr> { v,
- new LiteralExpr(Token.NoToken, Basetypes.BigNum.FromInt(1 << (upper+1)), 32) });
- }
- else
- {
- return Expr.Lt(v, Expr.Literal(1 << (upper+1)));
- }
- }
-
- bool IAbstractDomain.TypeCheck(List<Type> argTypes, out string msg)
- {
- msg = "";
- if (argTypes.Count != 1)
- {
- msg = "Illegal number of arguments, expecting 1";
- return false;
- }
- if (argTypes.Any(tt => !tt.IsInt && !tt.IsBv))
- {
- msg = "Illegal type, expecting int or bv";
- return false;
- }
- return true;
- }
- }
-
- // foo(x_i) = all equalities that hold
- public class EqualitiesDomain : IAbstractDomain
- {
- bool isBottom;
- List<HashSet<int>> equalities;
-
- public EqualitiesDomain(bool isBottom, List<HashSet<int>> eq)
- {
- this.isBottom = isBottom;
- this.equalities = eq;
- }
-
- public static IAbstractDomain GetBottom()
- {
- return new EqualitiesDomain(true, new List<HashSet<int>>());
- }
-
- IAbstractDomain IAbstractDomain.Bottom()
- {
- return GetBottom();
- }
-
- public IAbstractDomain MakeTop(out bool changed)
- {
- if (equalities.Count == 0)
- {
- changed = false;
- return this;
- }
- changed = true;
- return new EqualitiesDomain(false, new List<HashSet<int>>());
- }
-
- IAbstractDomain IAbstractDomain.Join(List<Model.Element> state)
- {
- // find the guys that are equal
- var eq = new List<HashSet<int>>();
- for (int i = 0; i < state.Count; i++)
- {
- var added = false;
- foreach (var s in eq)
- {
- var sv = s.First();
- if (state[i].ToString() == state[sv].ToString())
- {
- s.Add(i);
- added = true;
- break;
- }
- }
- if (!added) eq.Add(new HashSet<int>(new int[] { i }));
- }
-
- if (isBottom)
- {
- return new EqualitiesDomain(false, eq);
- }
-
- // intersect two partitions equalities and eq
- var m1 = GetMap(equalities, state.Count);
- var m2 = GetMap(eq, state.Count);
-
- for (int i = 0; i < state.Count; i++)
- m2[i] = new HashSet<int>(m2[i].Intersect(m1[i]));
-
-
- // map from representative to set
- var repToSet = new Dictionary<int, HashSet<int>>();
-
- for (int i = 0; i < state.Count; i++)
- {
- var rep = m2[i].Min();
- if (!repToSet.ContainsKey(rep))
- repToSet[rep] = m2[i];
- }
-
- var ret = new List<HashSet<int>>();
- repToSet.Values.Iter(s => ret.Add(s));
-
- return new EqualitiesDomain(false, ret);
- }
-
- Expr IAbstractDomain.Gamma(List<Expr> vars)
- {
- if (isBottom) return Expr.False;
- Expr ret = Expr.True;
- foreach (var eq in equalities.Select(hs => hs.ToList()))
- {
- if (eq.Count == 1) continue;
- var prev = eq[0];
- for (int i = 1; i < eq.Count; i++)
- {
- ret = Expr.And(ret, Expr.Eq(vars[prev], vars[eq[i]]));
- prev = eq[i];
- }
- }
- return ret;
- }
-
- bool IAbstractDomain.TypeCheck(List<Type> argTypes, out string msg)
- {
- msg = "";
- if (argTypes.Count == 0) return true;
- var ot = argTypes[0];
-
- if (argTypes.Any(tt => !tt.Equals(ot)))
- {
- msg = string.Format("Illegal type, expecting type {0}, got {1}", ot, argTypes.First(tt => !tt.Equals(ot)));
- return false;
- }
- return true;
- }
-
- private HashSet<int>[] GetMap(List<HashSet<int>> eq, int n)
- {
- var ret = new HashSet<int>[n];
- foreach (var s in eq)
- {
- foreach (var i in s)
- ret[i] = s;
- }
- return ret;
- }
- }
-
- // foo(a,b) \in {false, \not a, a ==> b, true}
- public class ImplicationDomain : IAbstractDomain
- {
- enum Val {FALSE, NOT_A, A_IMP_B, TRUE};
- Val val;
-
- private ImplicationDomain(Val val)
- {
- this.val = val;
- }
-
- public static ImplicationDomain GetBottom()
- {
- return new ImplicationDomain(Val.FALSE);
- }
-
- public IAbstractDomain Bottom()
- {
- return GetBottom();
- }
-
- public IAbstractDomain MakeTop(out bool changed)
- {
- if(val == Val.TRUE) {
- changed = false;
- return this;
- }
- changed = true;
- return new ImplicationDomain(Val.TRUE);
- }
-
- public IAbstractDomain Join(List<Model.Element> states)
- {
- Debug.Assert(states.Count == 2);
- var v1 = (states[0] as Model.Boolean).Value;
- var v2 = (states[1] as Model.Boolean).Value;
-
- if (val == Val.TRUE) return this;
-
- var that = Val.TRUE;
- if (!v1) that = Val.NOT_A;
- else if (!v1 || v2) that = Val.A_IMP_B;
-
- if (that == Val.TRUE || val == Val.FALSE)
- return new ImplicationDomain(that);
-
- // Now, neither this or that is FALSE or TRUE
- if (val == that)
- return this;
-
- Debug.Assert(val == Val.A_IMP_B || that == Val.A_IMP_B);
- return new ImplicationDomain(Val.A_IMP_B);
- }
-
- public Expr Gamma(List<Expr> vars)
- {
- Debug.Assert(vars.Count == 2);
-
- var v1 = vars[0];
- var v2 = vars[1];
-
- if (val == Val.FALSE) return Expr.False;
- if (val == Val.TRUE) return Expr.True;
- if (val == Val.NOT_A) return Expr.Not(v1);
- return Expr.Imp(v1, v2);
- }
-
- public bool TypeCheck(List<Type> argTypes, out string msg)
- {
- msg = "";
- if (argTypes.Count != 2)
- {
- msg = "Illegal number of arguments, expecting 2";
- return false;
- }
- if (argTypes.Any(tt => !tt.IsBool))
- {
- msg = "Illegal type, expecting bool";
- return false;
- }
- return true;
- }
- }
-
- public class ConstantProp : IAbstractDomain
- {
- object val;
- bool isBottom;
- bool isTop;
-
- private ConstantProp(object val, bool isTop, bool isBottom)
- {
- this.val = val;
- this.isBottom = isBottom;
- this.isTop = isTop;
- Debug.Assert(!(isTop && isBottom));
- Debug.Assert(val == null || (val is int) || (val is bool));
- }
-
- public static ConstantProp GetExtObj(object val)
- {
- Debug.Assert(val != null);
- return new ConstantProp(val, false, false);
- }
-
- public static ConstantProp GetTop()
- {
- return new ConstantProp(null, true, false);
- }
-
- public static ConstantProp GetBottom()
- {
- return new ConstantProp(null, false, true);
- }
-
- public IAbstractDomain MakeTop(out bool changed) {
- if (isTop)
- {
- changed = false;
- return this;
- }
- changed = true;
- return GetTop();
- }
-
- private ConstantProp Join(ConstantProp that)
- {
- if (isBottom) return that;
- if (isTop) return this;
- if (that.isBottom) return this;
- if (that.isTop) return that;
-
- if ((val is int) && !(that.val is int))
- throw new AbsHoudiniInternalError("Type mismatch in ExtObj");
-
- if ((val is bool) && !(that.val is bool))
- throw new AbsHoudiniInternalError("Type mismatch in ExtObj");
-
- if (val is int)
- {
- var v1 = (int)val;
- var v2 = (int)that.val;
- if (v1 != v2) return GetTop();
- return this;
- }
- else if (val is bool)
- {
- var v1 = (bool)val;
- var v2 = (bool)that.val;
- if (v1 != v2) return GetTop();
- return this;
- }
- throw new AbsHoudiniInternalError("Illegal val type in ExtObj");
- }
-
- public IAbstractDomain Bottom()
- {
- return GetBottom();
- }
-
- public IAbstractDomain Join(List<Model.Element> states)
- {
- Debug.Assert(states.Count == 1);
- var state = states[0];
- ConstantProp that = null;
-
- if (state is Model.Integer)
- {
- that = GetExtObj((state as Model.Integer).AsInt());
- }
- else if (state is Model.Boolean)
- {
- that = GetExtObj((state as Model.Boolean).Value);
- }
- else
- {
- throw new AbsHoudiniInternalError("Illegal type " + state.GetType().ToString());
- }
-
- return Join(that);
- }
-
- public Expr Gamma(List<Expr> vars)
- {
- Debug.Assert(vars.Count == 1);
- var v = vars[0];
- if (isBottom) return Expr.False;
- if (isTop) return Expr.True;
- if (val is int)
- return Expr.Eq(v, Expr.Literal((int)val));
- if (val is bool && (bool)val)
- return v;
- if (val is bool && !(bool)val)
- return Expr.Not(v);
-
- return null;
- }
-
- public bool TypeCheck(List<Type> argTypes, out string msg)
- {
- msg = "";
- if (argTypes.Count != 1)
- {
- msg = "Illegal number of arguments, expecting 1";
- return false;
- }
- if (!argTypes[0].IsInt && ! argTypes[0].IsBool)
- {
- msg = "Illegal type, expecting int or bool";
- return false;
- }
- return true;
- }
- }
-
-
- public class IndependentAttribute<T> : IAbstractDomain where T : class, IAbstractDomain
- {
- bool isBottom;
- int numVars;
- List<T> varVal;
- T underlyingInstance;
-
- public IndependentAttribute()
- {
- isBottom = true;
- numVars = 0;
- varVal = new List<T>();
- underlyingInstance = null;
- }
-
- public IAbstractDomain Bottom()
- {
- return new IndependentAttribute<T>();
- }
-
- public IAbstractDomain MakeTop(out bool changed)
- {
- var mt = new Func<IAbstractDomain>(() =>
- {
- var ret = new IndependentAttribute<T>();
- ret.isBottom = true;
- ret.numVars = numVars;
- ret.underlyingInstance = underlyingInstance;
- ret.varVal = new List<T>();
- bool tmp;
- for (int i = 0; i < varVal.Count; i++)
- ret.varVal.Add(varVal[i].MakeTop(out tmp) as T);
- return ret;
- });
-
- if (!isBottom)
- {
- foreach (var t in varVal)
- {
- var top = t.MakeTop(out changed);
- if (changed)
- {
- return mt();
- }
- }
- }
- else
- {
- changed = true;
- return mt();
- }
-
- changed = false;
- return this;
- }
- public IAbstractDomain Join(List<Model.Element> state)
- {
- SetUnderlyingInstance();
-
- if (!isBottom && numVars != state.Count)
- {
- throw new AbsHoudiniInternalError(
- string.Format("Got illegal number of arguments ({0}), expected {1}", state.Count, numVars));
- }
-
- var ret = new IndependentAttribute<T>();
- ret.isBottom = false;
- ret.numVars = state.Count;
- for(int i = 0; i < state.Count; i++)
- {
- var sl = new List<Model.Element>();
- sl.Add(state[i]);
- T prev = isBottom ? underlyingInstance.Bottom() as T : varVal[i];
- ret.varVal.Add(prev.Join(sl) as T);
- }
-
- return ret;
- }
-
- public Expr Gamma(List<Expr> vars)
- {
- if (isBottom) return Expr.False;
- if (numVars != vars.Count)
- throw new AbsHoudiniInternalError(
- string.Format("Got illegal number of arguments ({0}), expected {1}", vars.Count, numVars));
-
- Expr ret = Expr.True;
- for (int i = 0; i < numVars; i++)
- {
- var sl = new List<Expr>(); sl.Add(vars[i]);
- ret = Expr.And(ret, varVal[i].Gamma(sl));
- }
-
- return ret;
- }
-
- private void SetUnderlyingInstance()
- {
- if (underlyingInstance != null) return;
- var tt = typeof(T);
- underlyingInstance = AbstractDomainFactory.GetInstance(tt) as T;
- }
-
- public bool TypeCheck(List<Type> argTypes, out string msg)
- {
- SetUnderlyingInstance();
-
- msg = "";
- foreach(var t in argTypes)
- {
- if(!underlyingInstance.TypeCheck(new List<Type>(new Type[] { t }), out msg))
- return false;
- }
- return true;
- }
- }
-
- public class AbstractDomainFactory
- {
- // Type name -> Instance
- private static Dictionary<string, IAbstractDomain> abstractDomainInstances = new Dictionary<string, IAbstractDomain>();
- private static Dictionary<string, IAbstractDomain> abstractDomainInstancesFriendly = new Dictionary<string, IAbstractDomain>();
-
- // bitvector operations
- public static Dictionary<int, Function> bvslt = new Dictionary<int, Function>();
-
- public static void Register(string friendlyName, IAbstractDomain instance)
- {
- var Name = instance.GetType().FullName;
- Debug.Assert(!abstractDomainInstances.ContainsKey(Name));
- abstractDomainInstances.Add(Name, instance);
- abstractDomainInstancesFriendly.Add(friendlyName, instance);
- }
-
- public static IAbstractDomain GetInstance(System.Type type)
- {
- var Name = type.FullName;
- Debug.Assert(abstractDomainInstances.ContainsKey(Name));
- return abstractDomainInstances[Name] as IAbstractDomain;
- }
-
- public static IAbstractDomain GetInstance(string friendlyName)
- {
- if (!abstractDomainInstancesFriendly.ContainsKey(friendlyName))
- {
- Console.WriteLine("Domain {0} not found", friendlyName);
- Console.WriteLine("Supported domains are:");
- abstractDomainInstancesFriendly.Keys.Iter(tup => Console.WriteLine(" {0}", tup));
- throw new AbsHoudiniInternalError("Domain not found");
- }
- return abstractDomainInstancesFriendly[friendlyName] as IAbstractDomain;
- }
-
- public static void Initialize(Program program)
- {
- // Declare abstract domains
- var domains = new List<System.Tuple<string, IAbstractDomain>>(new System.Tuple<string, IAbstractDomain>[] {
- System.Tuple.Create("HoudiniConst", HoudiniConst.GetBottom() as IAbstractDomain),
- System.Tuple.Create("Intervals", new Intervals() as IAbstractDomain),
- System.Tuple.Create("ConstantProp", ConstantProp.GetBottom() as IAbstractDomain),
- System.Tuple.Create("PredicateAbs", new PredicateAbsElem() as IAbstractDomain),
- System.Tuple.Create("PredicateAbsFull", new PredicateAbsFullElem() as IAbstractDomain),
- System.Tuple.Create("ImplicationDomain", ImplicationDomain.GetBottom() as IAbstractDomain),
- System.Tuple.Create("PowDomain", PowDomain.GetBottom() as IAbstractDomain),
- System.Tuple.Create("EqualitiesDomain", EqualitiesDomain.GetBottom() as IAbstractDomain),
- System.Tuple.Create("IA[HoudiniConst]", new IndependentAttribute<HoudiniConst>() as IAbstractDomain),
- System.Tuple.Create("IA[ConstantProp]", new IndependentAttribute<ConstantProp>() as IAbstractDomain),
- System.Tuple.Create("IA[Intervals]", new IndependentAttribute<Intervals>() as IAbstractDomain),
- System.Tuple.Create("IA[PowDomain]", new IndependentAttribute<PowDomain>() as IAbstractDomain),
- });
-
- domains.Iter(tup => AbstractDomainFactory.Register(tup.Item1, tup.Item2));
- program.Functions.Iter(RegisterFunction);
- }
-
- private static void RegisterFunction(Function func)
- {
- var attr = QKeyValue.FindStringAttribute(func.Attributes, "bvbuiltin");
- if (attr != null && attr == "bvslt" && func.InParams.Count == 2 && func.InParams[0].TypedIdent.Type.IsBv)
- bvslt.Add(func.InParams[0].TypedIdent.Type.BvBits, func);
- }
- }
-
- public interface IAbstractDomain
- {
- IAbstractDomain Bottom();
- IAbstractDomain MakeTop(out bool changed);
- IAbstractDomain Join(List<Model.Element> state);
- Expr Gamma(List<Expr> vars);
- bool TypeCheck(List<Type> argTypes, out string msg);
- }
-
- public class AbstractHoudini
- {
- // Input Program
- Program program;
- // Impl -> VC
- Dictionary<string, VCExpr> impl2VC;
- // Impl -> Vars at end of the impl
- Dictionary<string, List<VCExpr>> impl2EndStateVars;
- // Impl -> (callee,summary pred)
- Dictionary<string, List<Tuple<string, bool, VCExprVar, VCExprNAry>>> impl2CalleeSummaries;
- // pointer to summary class
- ISummaryElement summaryClass;
- // impl -> summary
- Dictionary<string, ISummaryElement> impl2Summary;
- // name -> impl
- Dictionary<string, Implementation> name2Impl;
- // Use Bilateral algorithm
- public static bool UseBilateralAlgo = true;
- public static int iterTimeLimit = -1; // ms
-
- public static readonly string summaryPredSuffix = "SummaryPred";
-
- // Essentials: VCGen, Prover, and reporter
- VCGen vcgen;
- ProverInterface prover;
- AbstractHoudiniErrorReporter reporter;
-
- // Stats
- TimeSpan proverTime;
- int numProverQueries;
-
- // Produce witness for correctness: can be set programmatically
- public static string WitnessFile = "absHoudiniWitness.bpl";
-
- public AbstractHoudini(Program program)
- {
- this.program = program;
- this.impl2VC = new Dictionary<string, VCExpr>();
- this.impl2EndStateVars = new Dictionary<string, List<VCExpr>>();
- this.impl2CalleeSummaries = new Dictionary<string, List<Tuple<string, bool, VCExprVar, VCExprNAry>>>();
- this.impl2Summary = new Dictionary<string, ISummaryElement>();
- this.name2Impl = SimpleUtil.nameImplMapping(program);
-
- if (CommandLineOptions.Clo.ProverKillTime > 0)
- CommandLineOptions.Clo.ProverOptions = CommandLineOptions.Clo.ProverOptions.Concat1(string.Format("TIME_LIMIT={0}", CommandLineOptions.Clo.ProverKillTime));
-
- this.vcgen = new VCGen(program, CommandLineOptions.Clo.SimplifyLogFilePath, CommandLineOptions.Clo.SimplifyLogFileAppend, new List<Checker>());
- this.prover = ProverInterface.CreateProver(program, CommandLineOptions.Clo.SimplifyLogFilePath, CommandLineOptions.Clo.SimplifyLogFileAppend, -1);
-
- this.reporter = new AbstractHoudiniErrorReporter();
-
- this.proverTime = TimeSpan.Zero;
- this.numProverQueries = 0;
-
- if (CommandLineOptions.Clo.AbstractHoudini == "0")
- UseBilateralAlgo = false;
- }
-
- public void computeSummaries(ISummaryElement summaryClass)
- {
- // TODO: move this some place else
- PredicateAbs.FindUnsatPairs(prover.VCExprGen, prover);
-
- this.summaryClass = summaryClass;
-
- name2Impl.Values.Iter(attachEnsures);
-
- program.Implementations
- .Iter(impl => impl2Summary.Add(impl.Name, summaryClass.GetFlaseSummary(program, impl)));
-
- // Build call graph
- var Succ = new Dictionary<Implementation, HashSet<Implementation>>();
- var Pred = new Dictionary<Implementation, HashSet<Implementation>>();
- name2Impl.Values.Iter(impl => Succ.Add(impl, new HashSet<Implementation>()));
- name2Impl.Values.Iter(impl => Pred.Add(impl, new HashSet<Implementation>()));
-
- foreach (var impl in program.Implementations)
- {
- foreach (var blk in impl.Blocks)
- {
- foreach (var cmd in blk.Cmds.OfType<CallCmd>())
- {
- if (!name2Impl.ContainsKey(cmd.callee)) continue;
- Succ[impl].Add(name2Impl[cmd.callee]);
- Pred[name2Impl[cmd.callee]].Add(impl);
- }
- }
- }
-
- // Build SCC
- var sccs = new StronglyConnectedComponents<Implementation>(name2Impl.Values,
- new Adjacency<Implementation>(n => Pred[n]),
- new Adjacency<Implementation>(n => Succ[n]));
- sccs.Compute();
-
- // impl -> priority
- var impl2Priority = new Dictionary<string, int>();
- int p = 0;
- foreach (var scc in sccs)
- {
- foreach (var impl in scc)
- {
- impl2Priority.Add(impl.Name, p);
- p++;
- }
- }
-
-
- Inline();
-
- #region Witness generation setup
- // Create a copy of the program
- var copy = new Dictionary<string, Implementation>();
- if (WitnessFile != null)
- {
- foreach (var impl in program.Implementations)
- {
- var nimpl = new Implementation(Token.NoToken, impl.Name, impl.TypeParameters,
- impl.InParams, impl.OutParams, new List<Variable>(impl.LocVars), new List<Block>());
- foreach (var blk in impl.Blocks)
- {
- var cd = new CodeCopier();
- nimpl.Blocks.Add(new Block(Token.NoToken, blk.Label,
- cd.CopyCmdSeq(blk.Cmds), cd.CopyTransferCmd(blk.TransferCmd)));
- }
-
- copy.Add(impl.Name, nimpl);
- }
- }
- #endregion
-
- // Turn off subsumption. Why? Because then I see multiple occurences of the
- // attached ensures in the VC
- CommandLineOptions.Clo.UseSubsumption = CommandLineOptions.SubsumptionOption.Never;
-
- // Create all VCs
- name2Impl.Values
- .Iter(GenVC);
-
- // Start the iteration
- var worklist = new SortedSet<Tuple<int, Implementation>>();
- name2Impl.Values
- .Iter(impl => worklist.Add(Tuple.Create(impl2Priority[impl.Name], impl)));
-
- while (worklist.Any())
- {
- var impl = worklist.First().Item2;
- worklist.Remove(worklist.First());
-
- var changed = ProcessImpl(impl);
-
- if (changed)
- {
- Pred[impl].Where(pred => UseBilateralAlgo || pred != impl).Iter(pred => worklist.Add(Tuple.Create(impl2Priority[pred.Name], pred)));
- }
- }
-
- var allImpls = new SortedSet<Tuple<int, string>>();
- name2Impl.Values.Iter(impl => allImpls.Add(Tuple.Create(impl2Priority[impl.Name], impl.Name)));
- if (CommandLineOptions.Clo.Trace)
- {
- foreach (var tup in allImpls)
- {
- Console.WriteLine("Summary of {0}:", tup.Item2);
- Console.WriteLine("{0}", impl2Summary[tup.Item2]);
- }
- Console.WriteLine("Prover time = {0}", proverTime.TotalSeconds.ToString("F2"));
- Console.WriteLine("Number of prover queries = " + numProverQueries);
- }
-
- ProduceWitness(copy);
-
- prover.Close();
- CommandLineOptions.Clo.TheProverFactory.Close();
- }
-
- public HashSet<string> GetPredicates()
- {
- var ret = new HashSet<string>();
- prover = ProverInterface.CreateProver(program, CommandLineOptions.Clo.SimplifyLogFilePath, CommandLineOptions.Clo.SimplifyLogFileAppend, -1);
-
- foreach (var tup in impl2Summary)
- {
- var s = tup.Value as PredicateAbs;
- if (s == null) continue;
- ret.UnionWith(s.GetPredicates(program, prover.VCExprGen, prover));
- // debug output
- //Console.WriteLine("Summary of {0}:", tup.Key);
- //Console.WriteLine("{0}", tup.Value);
- }
-
- prover.Close();
- CommandLineOptions.Clo.TheProverFactory.Close();
- return ret;
- }
-
- // Obtain the summary expression for a procedure: used programmatically by clients
- // of AbstractHoudini
- public Expr GetSummary(Program program, Procedure proc)
- {
- if (!impl2Summary.ContainsKey(proc.Name))
- return Expr.True;
-
- var vars = new Dictionary<string, Expr>();
- foreach (var g in program.GlobalVariables)
- vars.Add(g.Name, Expr.Ident(g));
- foreach (var v in proc.InParams.OfType<Variable>())
- vars.Add(v.Name, Expr.Ident(v));
- foreach (var v in proc.OutParams.OfType<Variable>())
- vars.Add(v.Name, Expr.Ident(v));
-
- return impl2Summary[proc.Name].GetSummaryExpr(
- v => { if (vars.ContainsKey(v)) return vars[v]; else return null; },
- v => { if (vars.ContainsKey(v)) return new OldExpr(Token.NoToken, vars[v]); else return null; });
- }
-
- public ISummaryElement GetSummaryLowLevel(Procedure proc)
- {
- if (!impl2Summary.ContainsKey(proc.Name)) return null;
- return impl2Summary[proc.Name];
- }
-
- // Produce a witness that proves that the inferred annotations are correct
- private void ProduceWitness(Dictionary<string, Implementation> copy)
- {
- if (WitnessFile == null)
- return;
-
- foreach (var proc in program.Procedures)
- {
- var nensures = new List<Ensures>();
- proc.Ensures.OfType<Ensures>()
- .Where(ens => !QKeyValue.FindBoolAttribute(ens.Attributes, "ah") &&
- !QKeyValue.FindBoolAttribute(ens.Attributes, "pre") &&
- !QKeyValue.FindBoolAttribute(ens.Attributes, "post") &&
- QKeyValue.FindStringAttribute(ens.Attributes, "pre") == null &&
- QKeyValue.FindStringAttribute(ens.Attributes, "post") == null)
- .Iter(ens => nensures.Add(ens));
- foreach (Ensures en in nensures)
- en.Attributes = removeAttr("InlineAssume", en.Attributes);
-
- proc.Ensures = nensures;
- }
-
- var decls = new List<Declaration>(copy.Values);
- decls.AddRange(program.TopLevelDeclarations.Where(decl => !(decl is Implementation)));
- program.TopLevelDeclarations = decls;
- var name2Proc = new Dictionary<string, Procedure>();
- foreach (var proc in program.Procedures)
- {
- name2Proc.Add(proc.Name, proc);
- if (impl2Summary.ContainsKey(proc.Name))
- {
- var ens = new Ensures(false,
- impl2Summary[proc.Name].GetSummaryExpr(
- new Func<string, Expr>(s => null), new Func<string, Expr>(s => null)));
- ens.Attributes = new QKeyValue(Token.NoToken, "inferred", new List<object>(), ens.Attributes);
- proc.Ensures.Add(ens);
- }
- }
-
- using (var wt = new TokenTextWriter(WitnessFile, /*pretty=*/ false))
- {
- program.Emit(wt);
- }
-
- // Replace SummaryPreds with their definition
- foreach (var impl in program.Implementations)
- {
- foreach (var blk in impl.Blocks)
- {
- foreach (var cmd in blk.Cmds.OfType<AssumeCmd>())
- {
- var expr = cmd.Expr as NAryExpr;
- if (expr == null) continue;
- var op = expr.Fun as FunctionCall;
- if (op == null || !op.FunctionName.EndsWith(summaryPredSuffix)) continue;
- var calleeName = op.FunctionName.Substring(0, op.FunctionName.Length - summaryPredSuffix.Length);
- if (!impl2Summary.ContainsKey(calleeName)) continue;
- var callee = name2Impl[calleeName];
-
- // variable order: globals, ins, outs, modifies
- var forold = new Dictionary<string, Expr>();
- var always = new Dictionary<string, Expr>();
- int i = 0;
- foreach (var g in program.GlobalVariables)
- {
- forold.Add(g.Name, expr.Args[i]);
- always.Add(g.Name, expr.Args[i]);
- i++;
- }
- foreach (var v in callee.InParams.OfType<Variable>())
- {
- always.Add(v.Name, expr.Args[i]);
- i++;
- }
- foreach (var v in callee.OutParams.OfType<Variable>())
- {
- always.Add(v.Name, expr.Args[i]);
- i++;
- }
- foreach (var ie in name2Proc[calleeName].Modifies.OfType<IdentifierExpr>())
- {
- always[ie.Name] = expr.Args[i];
- i++;
- }
-
- cmd.Expr = impl2Summary[calleeName].GetSummaryExpr(
- v => { if (always.ContainsKey(v)) return always[v]; else return null; },
- v => { if (forold.ContainsKey(v)) return forold[v]; else return null; });
- }
- }
- }
-
- using (var wt = new TokenTextWriter(WitnessFile, /*pretty=*/ false))
- {
- program.Emit(wt);
- }
- if (CommandLineOptions.Clo.Trace) Console.WriteLine("Witness written to {0}", WitnessFile);
- }
-
- private QKeyValue removeAttr(string key, QKeyValue attr)
- {
- if (attr == null) return attr;
- if (attr.Key == key) return removeAttr(key, attr.Next);
- attr.Next = removeAttr(key, attr.Next);
- return attr;
- }
-
- private void Inline()
- {
- if (CommandLineOptions.Clo.InlineDepth < 0)
- return;
-
- var callGraph = BuildCallGraph();
-
- foreach (Implementation impl in callGraph.Nodes)
- {
- InlineEnsuresVisitor inlineEnsuresVisitor = new InlineEnsuresVisitor();
- inlineEnsuresVisitor.Visit(impl);
- }
-
- foreach (Implementation impl in callGraph.Nodes)
- {
- impl.OriginalBlocks = impl.Blocks;
- impl.OriginalLocVars = impl.LocVars;
- }
- foreach (Implementation impl in callGraph.Nodes)
- {
- CommandLineOptions.Inlining savedOption = CommandLineOptions.Clo.ProcedureInlining;
- CommandLineOptions.Clo.ProcedureInlining = CommandLineOptions.Inlining.Spec;
- Inliner.ProcessImplementationForHoudini(program, impl);
- CommandLineOptions.Clo.ProcedureInlining = savedOption;
- }
- foreach (Implementation impl in callGraph.Nodes)
- {
- impl.OriginalBlocks = null;
- impl.OriginalLocVars = null;
- }
-
- Graph<Implementation> oldCallGraph = callGraph;
- callGraph = new Graph<Implementation>();
- foreach (Implementation impl in oldCallGraph.Nodes)
- {
- callGraph.AddSource(impl);
- }
- foreach (Tuple<Implementation, Implementation> edge in oldCallGraph.Edges)
- {
- callGraph.AddEdge(edge.Item1, edge.Item2);
- }
- int count = CommandLineOptions.Clo.InlineDepth;
- while (count > 0)
- {
- foreach (Implementation impl in oldCallGraph.Nodes)
- {
- List<Implementation> newNodes = new List<Implementation>();
- foreach (Implementation succ in callGraph.Successors(impl))
- {
- newNodes.AddRange(oldCallGraph.Successors(succ));
- }
- foreach (Implementation newNode in newNodes)
- {
- callGraph.AddEdge(impl, newNode);
- }
- }
- count--;
- }
- }
-
- private Graph<Implementation> BuildCallGraph()
- {
- Graph<Implementation> callGraph = new Graph<Implementation>();
- Dictionary<Procedure, HashSet<Implementation>> procToImpls = new Dictionary<Procedure, HashSet<Implementation>>();
- foreach (var proc in program.Procedures)
- {
- procToImpls[proc] = new HashSet<Implementation>();
- }
- foreach (var impl in program.Implementations)
- {
- if (impl.SkipVerification) continue;
- callGraph.AddSource(impl);
- procToImpls[impl.Proc].Add(impl);
- }
- foreach (var impl in program.Implementations)
- {
- if (impl.SkipVerification) continue;
- foreach (Block b in impl.Blocks)
- {
- foreach (Cmd c in b.Cmds)
- {
- CallCmd cc = c as CallCmd;
- if (cc == null) continue;
- foreach (Implementation callee in procToImpls[cc.Proc])
- {
- callGraph.AddEdge(impl, callee);
- }
- }
- }
- }
- return callGraph;
- }
-
-
- private bool ProcessImpl(Implementation impl)
- {
- var ret = false;
- var gen = prover.VCExprGen;
-
- // construct summaries
- var env = VCExpressionGenerator.True;
- foreach (var tup in impl2CalleeSummaries[impl.Name])
- {
- // Not Bilateral: then reject self predicates
- if (UseBilateralAlgo == false && tup.Item1 == impl.Name)
- continue;
-
- // Bilateral: only reject self summary
- if (UseBilateralAlgo == true && tup.Item1 == impl.Name && tup.Item2)
- continue;
-
- var calleeSummary =
- impl2Summary[tup.Item1].GetSummaryExpr(
- GetVarMapping(name2Impl[tup.Item1], tup.Item4), prover.VCExprGen);
- env = gen.AndSimp(env, gen.Eq(tup.Item3, calleeSummary));
- }
-
- var prev = impl2Summary[impl.Name].Copy();
- var upper = impl2Summary[impl.Name].GetTrueSummary(program, impl);
- var sw = new Stopwatch();
- sw.Start();
- var lowerTime = TimeSpan.Zero;
-
- while(true)
- {
- var usedLower = true;
- var query = impl2Summary[impl.Name];
- sw.Restart();
-
- // construct self summaries
- var summaryExpr = VCExpressionGenerator.True;
- foreach (var tup in impl2CalleeSummaries[impl.Name])
- {
- if (UseBilateralAlgo == false && tup.Item1 != impl.Name)
- continue;
- if (UseBilateralAlgo == true && (tup.Item1 != impl.Name || !tup.Item2))
- continue;
-
- if (UseBilateralAlgo)
- {
- query = query.AbstractConsequence(upper);
- if (query == null) query = impl2Summary[tup.Item1];
- else usedLower = false;
- }
-
- var ts =
- query.GetSummaryExpr(
- GetVarMapping(name2Impl[tup.Item1], tup.Item4), prover.VCExprGen);
- summaryExpr = gen.AndSimp(summaryExpr, gen.Eq(tup.Item3, ts));
- }
- //Console.WriteLine("Trying summary for {0}: {1}", impl.Name, summaryExpr);
-
- reporter.model = null;
- var vc = gen.AndSimp(env, summaryExpr);
- vc = gen.Implies(vc, impl2VC[impl.Name]);
-
- //Console.WriteLine("Checking: {0}", vc);
- if (CommandLineOptions.Clo.Trace)
- Console.WriteLine("Verifying {0} ({1}): {2}", impl.Name, usedLower ? "lower" : "ac", query);
-
- if (usedLower && lowerTime.TotalMilliseconds >= iterTimeLimit && iterTimeLimit >= 0)
- {
- if (UseBilateralAlgo)
- {
- if (CommandLineOptions.Clo.Trace)
- Console.WriteLine("Timeout/Spaceout while verifying " + impl.Name);
- ret = prev.IsEqual(upper) ? false : true;
- impl2Summary[impl.Name] = upper;
- break;
- }
- else
- {
- if (CommandLineOptions.Clo.Trace)
- Console.WriteLine("Timeout/Spaceout while verifying " + impl.Name);
- var tt = impl2Summary[impl.Name].GetTrueSummary(program, impl);
- ret = prev.IsEqual(tt) ? false : true; ;
- impl2Summary[impl.Name] = tt;
- break;
- }
- }
-
- var start = DateTime.Now;
-
- //prover.Push();
- //prover.Assert(gen.Not(vc), true);
- //prover.FlushAxiomsToTheoremProver();
- //prover.Check();
- //ProverInterface.Outcome proverOutcome = prover.CheckOutcome(reporter);
- //prover.Pop();
-
- prover.BeginCheck(impl.Name, vc, reporter);
- ProverInterface.Outcome proverOutcome = prover.CheckOutcome(reporter);
-
- var inc = (DateTime.Now - start);
- proverTime += inc;
- numProverQueries++;
-
- sw.Stop();
- if (usedLower) lowerTime += sw.Elapsed;
-
- if(CommandLineOptions.Clo.Trace)
- Console.WriteLine("Time taken = " + inc.TotalSeconds.ToString());
-
- if (UseBilateralAlgo)
- {
- if (proverOutcome == ProverInterface.Outcome.TimeOut || proverOutcome == ProverInterface.Outcome.OutOfMemory)
- {
- if(CommandLineOptions.Clo.Trace)
- Console.WriteLine("Timeout/Spaceout while verifying " + impl.Name);
- ret = prev.IsEqual(upper) ? false : true;
- impl2Summary[impl.Name] = upper;
- break;
- }
-
- if (reporter.model == null && usedLower)
- break;
-
- if (reporter.model == null)
- {
- upper.Meet(query);
- }
- else
- {
- var state = CollectState(impl);
- impl2Summary[impl.Name].Join(state, reporter.model);
- ret = true;
- }
- }
- else
- {
- if (proverOutcome == ProverInterface.Outcome.TimeOut || proverOutcome == ProverInterface.Outcome.OutOfMemory)
- {
- if (CommandLineOptions.Clo.Trace)
- Console.WriteLine("Timeout/Spaceout while verifying " + impl.Name);
- var tt = impl2Summary[impl.Name].GetTrueSummary(program, impl);
- ret = prev.IsEqual(tt) ? false : true; ;
- impl2Summary[impl.Name] = tt;
- break;
- }
-
- if (reporter.model == null)
- break;
- //reporter.model.Write(Console.Out);
-
- var state = CollectState(impl);
- impl2Summary[impl.Name].Join(state, reporter.model);
- ret = true;
- }
- }
- return ret;
- }
-
- private Dictionary<string, VCExpr> GetVarMapping(Implementation impl, VCExprNAry summaryPred)
- {
- var ret = new Dictionary<string, VCExpr>();
-
- var cnt = 0;
- foreach (var g in program.GlobalVariables)
- {
- ret.Add(string.Format("old({0})", g.Name), summaryPred[cnt]);
- cnt++;
- }
- foreach (var v in impl.InParams.OfType<Variable>().Concat(
- impl.OutParams.OfType<Variable>().Concat(
- impl.Proc.Modifies.OfType<IdentifierExpr>().Select(ie => ie.Decl))))
- {
- ret.Add(v.Name, summaryPred[cnt]);
- cnt++;
- }
-
- // Fill up values of globals that are not modified
- cnt = 0;
- foreach (var g in program.GlobalVariables)
- {
- if (ret.ContainsKey(g.Name)) { cnt++; continue; }
-
- ret.Add(string.Format("{0}", g.Name), summaryPred[cnt]);
- cnt++;
- }
-
- // Constants
- foreach (var c in program.Constants)
- {
- var value = prover.Context.BoogieExprTranslator.Translate(Expr.Ident(c));
- ret.Add(string.Format("{0}", c.Name), value);
- ret.Add(string.Format("old({0})", c.Name), value);
- }
-
- return ret;
- }
-
- private Dictionary<string, Model.Element> CollectState(Implementation impl)
- {
- var ret = new Dictionary<string, Model.Element>();
-
- var model = reporter.model;
- var implVars = impl2EndStateVars[impl.Name];
-
- var cnt = 0;
- foreach (var g in program.GlobalVariables)
- {
- ret.Add(string.Format("old({0})", g.Name), getValue(implVars[cnt], model));
- cnt++;
- }
- foreach (var v in impl.InParams.OfType<Variable>().Concat(
- impl.OutParams.OfType<Variable>().Concat(
- impl.Proc.Modifies.OfType<IdentifierExpr>().Select(ie => ie.Decl))))
- {
- ret.Add(v.Name, getValue(implVars[cnt], model));
- cnt++;
- }
-
- // Fill up values of globals that are not modified
- cnt = 0;
- foreach (var g in program.GlobalVariables)
- {
- if (ret.ContainsKey(g.Name)) { cnt++; continue; }
-
- ret.Add(string.Format("{0}", g.Name), getValue(implVars[cnt], model));
- cnt++;
- }
-
- // Constants
- foreach (var c in program.Constants)
- {
- try
- {
- var value = getValue(prover.Context.BoogieExprTranslator.Translate(Expr.Ident(c)), model);
- ret.Add(string.Format("{0}", c.Name), value);
- ret.Add(string.Format("old({0})", c.Name), value);
- }
- catch (Exception)
- {
- // constant not assigned a value: add a default value
- Model.Element value = null;
- if (c.TypedIdent.Type.IsInt)
- value = model.MkIntElement(0);
- else if (c.TypedIdent.Type.IsBool)
- value = model.MkElement("false");
-
- ret.Add(string.Format("{0}", c.Name), value);
- ret.Add(string.Format("old({0})", c.Name), value);
- }
- }
-
- return ret;
- }
-
- private Model.Element getValue(VCExpr arg, Model model)
- {
- if (arg is VCExprLiteral)
- {
- //return model.GetElement(arg.ToString());
- return model.MkElement(arg.ToString());
- }
- else if (arg is VCExprVar)
- {
- var el = model.TryGetFunc(prover.Context.Lookup(arg as VCExprVar));
- if (el != null)
- {
- Debug.Assert(el.Arity == 0 && el.AppCount == 1);
- return el.Apps.First().Result;
- }
- else
- {
- // Variable not defined; assign arbitrary value
- if (arg.Type.IsBool)
- return model.MkElement("false");
- else if (arg.Type.IsInt)
- return model.MkIntElement(0);
- else
- return null;
- }
- }
- else
- {
- Debug.Assert(false);
- return null;
- }
- }
-
- private void attachEnsures(Implementation impl)
- {
- List<Variable> functionInterfaceVars = new List<Variable>();
- foreach (Variable v in vcgen.program.GlobalVariables)
- {
- functionInterfaceVars.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", v.TypedIdent.Type), true));
- }
- foreach (Variable v in impl.InParams)
- {
- functionInterfaceVars.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", v.TypedIdent.Type), true));
- }
- foreach (Variable v in impl.OutParams)
- {
- functionInterfaceVars.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", v.TypedIdent.Type), true));
- }
- foreach (IdentifierExpr e in impl.Proc.Modifies)
- {
- if (e.Decl == null) continue;
- functionInterfaceVars.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", e.Decl.TypedIdent.Type), true));
- }
- Formal returnVar = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", Bpl.Type.Bool), false);
- var function = new Function(Token.NoToken, impl.Name + summaryPredSuffix, functionInterfaceVars, returnVar);
- prover.Context.DeclareFunction(function, "");
-
- List<Expr> exprs = new List<Expr>();
- foreach (Variable v in vcgen.program.GlobalVariables)
- {
- Contract.Assert(v != null);
- exprs.Add(new OldExpr(Token.NoToken, new IdentifierExpr(Token.NoToken, v)));
- }
- foreach (Variable v in impl.Proc.InParams)
- {
- Contract.Assert(v != null);
- exprs.Add(new IdentifierExpr(Token.NoToken, v));
- }
- foreach (Variable v in impl.Proc.OutParams)
- {
- Contract.Assert(v != null);
- exprs.Add(new IdentifierExpr(Token.NoToken, v));
- }
- foreach (IdentifierExpr ie in impl.Proc.Modifies)
- {
- Contract.Assert(ie != null);
- if (ie.Decl == null)
- continue;
- exprs.Add(ie);
- }
- Expr postExpr = new NAryExpr(Token.NoToken, new FunctionCall(function), exprs);
- impl.Proc.Ensures.Add(
- new Ensures(Token.NoToken, false, postExpr, "",
- new QKeyValue(Token.NoToken, "ah", new List<object>(), null)));
- }
-
- private void GenVC(Implementation impl)
- {
- ModelViewInfo mvInfo;
- Dictionary<int, Absy> label2absy;
-
- if (CommandLineOptions.Clo.Trace)
- {
- Console.WriteLine("Generating VC of {0}", impl.Name);
- }
-
- vcgen.ConvertCFG2DAG(impl);
- vcgen.PassifyImpl(impl, out mvInfo);
-
- var gen = prover.VCExprGen;
- var vcexpr = vcgen.GenerateVC(impl, null, out label2absy, prover.Context);
-
-
- // Find the assert
- impl2EndStateVars.Add(impl.Name, new List<VCExpr>());
- var found = false;
- var assertId = -1;
- foreach (var blk in impl.Blocks)
- {
- foreach (var cmd in blk.Cmds.OfType<AssertCmd>())
- {
- if (SimpleUtil.isAssertTrue(cmd)) continue;
- var nary = cmd.Expr as NAryExpr;
- if (nary == null) continue;
- var pred = nary.Fun as FunctionCall;
- if (pred == null || pred.FunctionName != (impl.Name + (AbstractHoudini.summaryPredSuffix)))
- continue;
-
- Debug.Assert(!found);
- found = true;
- assertId = cmd.UniqueId;
- //Console.WriteLine("assert cmd id: {0}", cmd.UniqueId);
- nary.Args.OfType<Expr>()
- .Iter(expr => impl2EndStateVars[impl.Name].Add(prover.Context.BoogieExprTranslator.Translate(expr)));
- }
- }
-
- // It is possible that no assert is found in the procedure. It happens when the
- // procedure doesn't return.
- //Debug.Assert(found);
-
- // Grab summary predicates
- var visitor = new FindSummaryPred(prover.VCExprGen, assertId);
- vcexpr = visitor.Mutate(vcexpr, true);
-
- // Create a macro so that the VC can sit with the theorem prover
- Macro macro = new Macro(Token.NoToken, impl.Name + "Macro", new List<Variable>(), new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", Bpl.Type.Bool), false));
- prover.DefineMacro(macro, vcexpr);
-
- // Store VC
- impl2VC.Add(impl.Name, gen.Function(macro));
-
- //Console.WriteLine("VC of {0}: {1}", impl.Name, vcexpr);
-
- // check sanity: only one predicate for self-summary
- // (There may be none when the procedure doesn't return)
- Debug.Assert(visitor.summaryPreds.Count(tup => tup.Item2) <= 1);
-
- impl2CalleeSummaries.Add(impl.Name, new List<Tuple<string, bool, VCExprVar, VCExprNAry>>());
- visitor.summaryPreds.Iter(tup => impl2CalleeSummaries[impl.Name].Add(tup));
- }
- }
-
- public interface ISummaryElement
- {
- ISummaryElement Copy();
- ISummaryElement GetFlaseSummary(Program program, Implementation impl);
- void Join(Dictionary<string, Model.Element> state, Model model);
- VCExpr GetSummaryExpr(Dictionary<string, VCExpr> incarnations, VCExpressionGenerator gen);
- Expr GetSummaryExpr(Func<string, Expr> always, Func<string, Expr> forold);
-
- // For Bilateral
- ISummaryElement GetTrueSummary(Program program, Implementation impl);
- void Meet(ISummaryElement other);
- bool IsEqual(ISummaryElement other);
- ISummaryElement AbstractConsequence(ISummaryElement upper);
- }
-
- public class ConstantVal : ISummaryElement
- {
- Program program;
- Implementation impl;
- // var -> const set
- Dictionary<string, HashSet<int>> val;
- // set of vars
- HashSet<Variable> vars;
-
- public static readonly int MAX = 3;
-
- public ConstantVal()
- {
- // this is just a place holder
- val = new Dictionary<string, HashSet<int>>();
- vars = new HashSet<Variable>();
- }
-
- private ConstantVal(Program program, Implementation impl)
- {
- this.program = program;
- this.impl = impl;
- this.val = new Dictionary<string, HashSet<int>>();
-
- vars = new HashSet<Variable>();
- impl.Proc.Modifies
- .OfType<IdentifierExpr>()
- .Select(ie => ie.Decl)
- .Where(v => v.TypedIdent.Type.IsInt)
- .Iter(v => vars.Add(v));
- impl.OutParams.OfType<Variable>()
- .Where(v => v.TypedIdent.Type.IsInt)
- .Iter(v => vars.Add(v));
-
- vars.Iter(v => val.Add(v.Name, null));
- }
-
-
- public void Join(Dictionary<string, Model.Element> state, Model model)
- {
- foreach (var vv in vars)
- {
- var v = vv.Name;
- var newv = state[v].AsInt();
- var oldv = val[v];
-
- if (oldv == null)
- {
- val[v] = new HashSet<int>();
- val[v].Add(newv);
- }
- else if(oldv.Count > 0)
- {
- val[v].Add(newv);
- if (val[v].Count > MAX)
- val[v] = new HashSet<int>();
- }
-
- }
- }
-
- public VCExpr GetSummaryExpr(Dictionary<string, VCExpr> incarnations, VCExpressionGenerator gen)
- {
- VCExpr ret = VCExpressionGenerator.True;
- if (val.Values.Any(v => v == null))
- return VCExpressionGenerator.False;
-
- foreach (var v in vars)
- {
- var consts = val[v.Name];
- Debug.Assert(consts != null);
-
- if (consts.Count == 0)
- continue;
-
- var vexpr = VCExpressionGenerator.False;
- consts.Iter(c => vexpr = gen.OrSimp(vexpr, gen.Eq(incarnations[v.Name], gen.Integer(Microsoft.Basetypes.BigNum.FromInt(c)))));
- ret = gen.AndSimp(ret, vexpr);
- }
-
- return ret;
- }
-
- public override string ToString()
- {
- var ret = "true";
- if (val.Values.Any(v => v == null))
- return "false";
-
- foreach (var v in vars)
- {
- var consts = val[v.Name];
- Debug.Assert(consts != null);
-
- if (consts.Count == 0)
- continue;
-
- var vexpr = "false";
- consts.Iter(c => vexpr =
- string.Format("{0} OR ({1} == {2})", vexpr, v.Name, c));
-
- ret = string.Format("{0} AND ({1})", ret, vexpr);
- }
-
- return ret;
- }
-
-
- public ISummaryElement GetFlaseSummary(Program program, Implementation impl)
- {
- return new ConstantVal(program, impl);
- }
-
- #region ISummaryElement (Bilateral) Members
-
-
- public ISummaryElement GetTrueSummary(Program program, Implementation impl)
- {
- throw new NotImplementedException();
- }
-
- public void Meet(ISummaryElement other)
- {
- throw new NotImplementedException();
- }
-
- public bool IsEqual(ISummaryElement other)
- {
- throw new NotImplementedException();
- }
-
- public ISummaryElement AbstractConsequence(ISummaryElement upper)
- {
- throw new NotImplementedException();
- }
-
- #endregion
-
- #region ISummaryElement Members
-
-
- public Expr GetSummaryExpr(Func<string, Expr> always, Func<string, Expr> forold)
- {
- throw new NotImplementedException();
- }
-
- #endregion
-
- #region ISummaryElement Members
-
- public ISummaryElement Copy()
- {
- throw new NotImplementedException();
- }
-
- #endregion
- }
-
- public class NamedExpr
- {
- public string name;
- public Expr expr;
-
- public NamedExpr(string name, Expr expr)
- {
- this.name = name;
- this.expr = expr;
- }
-
- public NamedExpr(Expr expr)
- {
- this.name = null;
- this.expr = expr;
- }
-
- public override string ToString()
- {
- if (name != null)
- return name;
-
- return expr.ToString();
- }
- }
-
- public class PredicateAbs : ISummaryElement
- {
- public static Dictionary<string, List<NamedExpr>> PrePreds { get; private set; }
- public static Dictionary<string, HashSet<int>> PosPrePreds { get; private set; }
- public static Dictionary<string, List<NamedExpr>> PostPreds { get; private set; }
- public static Dictionary<Tuple<string, int>, List<PredicateAbsDisjunct>> UpperCandidates;
- private static HashSet<string> boolConstants;
- // {proc, pred-pair} -> polariry
- public static HashSet<Tuple<string, int, int, bool, bool>> unsatPrePredPairs;
- public static HashSet<Tuple<string, int, int, bool, bool>> unsatPostPredPairs;
-
- // Temporary: used during eval
- private static Model model = null;
-
- string procName;
- PredicateAbsDisjunct[] value;
- bool isFalse;
-
- public PredicateAbs(string procName)
- {
- this.procName = procName;
- isFalse = true;
- value = new PredicateAbsDisjunct[PostPreds[this.procName].Count];
- for (int i = 0; i < PostPreds[this.procName].Count; i++) value[i] = null;
- }
-
- public static void Initialize(Program program)
- {
- PrePreds = new Dictionary<string, List<NamedExpr>>();
- PostPreds = new Dictionary<string, List<NamedExpr>>();
- PosPrePreds = new Dictionary<string, HashSet<int>>();
-
- boolConstants = new HashSet<string>();
- UpperCandidates = new Dictionary<Tuple<string, int>, List<PredicateAbsDisjunct>>();
-
- program.Constants
- .Where(c => c.TypedIdent.Type.IsBool)
- .Iter(c => boolConstants.Add(c.Name));
-
- // Add template pre-post to all procedures
- var preT = new List<NamedExpr>();
- var postT = new List<NamedExpr>();
- var posPreT = new HashSet<int>();
- var tempP = new HashSet<Procedure>();
- foreach (var proc in
- program.Procedures
- .Where(proc => QKeyValue.FindBoolAttribute(proc.Attributes, "template")))
- {
- tempP.Add(proc);
- foreach (var ens in proc.Ensures.OfType<Ensures>())
- {
- var pos = QKeyValue.FindBoolAttribute(ens.Attributes, "positive");
-
- if (QKeyValue.FindBoolAttribute(ens.Attributes, "pre"))
- {
- preT.Add(new NamedExpr(null, ens.Condition));
- if (pos) posPreT.Add(preT.Count - 1);
- }
-
- if (QKeyValue.FindBoolAttribute(ens.Attributes, "post"))
- postT.Add(new NamedExpr(null, ens.Condition));
-
- var s = QKeyValue.FindStringAttribute(ens.Attributes, "pre");
- if (s != null)
- {
- preT.Add(new NamedExpr(s, ens.Condition));
- if (pos) posPreT.Add(preT.Count - 1);
- }
-
- s = QKeyValue.FindStringAttribute(ens.Attributes, "post");
- if (s != null)
- postT.Add(new NamedExpr(s, ens.Condition));
- }
- }
-
- program.RemoveTopLevelDeclarations(decl => tempP.Contains(decl));
- var upperPreds = new Dictionary<string, List<Expr>>();
-
- foreach (var impl in program.Implementations)
- {
- PrePreds.Add(impl.Name, new List<NamedExpr>());
- PostPreds.Add(impl.Name, new List<NamedExpr>());
- PosPrePreds.Add(impl.Name, new HashSet<int>());
-
- // Add "false" as the first post predicate
- //PostPreds[impl.Name].Add(new NamedExpr(Expr.False));
-
- preT.Iter(e => PrePreds[impl.Name].Add(e));
- postT.Iter(e => PostPreds[impl.Name].Add(e));
- PosPrePreds[impl.Name].UnionWith(posPreT);
-
- // Pick up per-procedure pre-post
- var nens = new List<Ensures>();
- foreach (var ens in impl.Proc.Ensures.OfType<Ensures>())
- {
- string s = null;
- var pos = QKeyValue.FindBoolAttribute(ens.Attributes, "positive");
-
- if (QKeyValue.FindBoolAttribute(ens.Attributes, "pre"))
- {
- PrePreds[impl.Name].Add(new NamedExpr(ens.Condition));
- PosPrePreds[impl.Name].Add(PrePreds[impl.Name].Count - 1);
- }
- else if (QKeyValue.FindBoolAttribute(ens.Attributes, "post"))
- {
- PostPreds[impl.Name].Add(new NamedExpr(ens.Condition));
- }
- else if ((s = QKeyValue.FindStringAttribute(ens.Attributes, "pre")) != null)
- {
- PrePreds[impl.Name].Add(new NamedExpr(s, ens.Condition));
- PosPrePreds[impl.Name].Add(PrePreds[impl.Name].Count - 1);
- }
- else if ((s = QKeyValue.FindStringAttribute(ens.Attributes, "post")) != null)
- {
- PostPreds[impl.Name].Add(new NamedExpr(s, ens.Condition));
- }
- else if (QKeyValue.FindBoolAttribute(ens.Attributes, "upper"))
- {
- var key = impl.Name;
- if (!upperPreds.ContainsKey(key))
- upperPreds.Add(key, new List<Expr>());
- upperPreds[key].Add(ens.Condition);
- }
- else
- {
- nens.Add(ens);
- }
- }
- impl.Proc.Ensures = nens;
- }
-
- foreach (var tup in upperPreds)
- {
- var procName = tup.Key;
- var candidates = tup.Value;
- if (!candidates.Any()) continue;
-
- var strToPost = new Dictionary<string, int>();
- for (int i = 0; i < PostPreds[procName].Count; i++)
- {
- strToPost.Add(PostPreds[procName][i].expr.ToString(), i);
- }
-
- foreach (var expr in candidates)
- {
- if (strToPost.ContainsKey(expr.ToString()))
- {
- var key = Tuple.Create(procName, strToPost[expr.ToString()]);
- if (!UpperCandidates.ContainsKey(key))
- UpperCandidates.Add(key, new List<PredicateAbsDisjunct>());
- UpperCandidates[key].Add(new PredicateAbsDisjunct(true, procName));
- }
- else
- {
- // Try parsing the expression as (pre-conjunct ==> post-pred)
- var parsed = ParseExpr(expr, procName);
- if (parsed != null && strToPost.ContainsKey(parsed.Item2.ToString()))
- {
- var key = Tuple.Create(procName, strToPost[parsed.Item2.ToString()]);
- if (!UpperCandidates.ContainsKey(key))
- UpperCandidates.Add(key, new List<PredicateAbsDisjunct>());
- UpperCandidates[key].Add(parsed.Item1);
- }
- }
- }
-
- }
- //Console.WriteLine("Running Abstract Houdini");
- //PostPreds.Iter(expr => Console.WriteLine("\tPost: {0}", expr));
- //PrePreds.Iter(expr => Console.WriteLine("\tPre: {0}", expr));
- }
-
- // Try parsing the expression as (pre-conjunct ==> post-pred)
- private static Tuple<PredicateAbsDisjunct, Expr> ParseExpr(Expr expr, string procName)
- {
- Expr postExpr = null;
- Expr preExpr = null;
-
- // Decompose outer Implies
- var nexpr = expr as NAryExpr;
- if (nexpr != null && (nexpr.Fun is BinaryOperator)
- && (nexpr.Fun as BinaryOperator).Op == BinaryOperator.Opcode.Imp
- && (nexpr.Args.Count == 2))
- {
- postExpr = nexpr.Args[1];
- preExpr = nexpr.Args[0];
- }
- else
- {
- if(CommandLineOptions.Clo.Trace) Console.WriteLine("Failed to parse {0} (ignoring)", expr);
- return null;
- }
-
-
- var atoms = DecomposeOuterAnd(preExpr);
- var pos = new HashSet<int>();
- var neg = new HashSet<int>();
-
- foreach (var atom in atoms)
- {
- var index = PrePreds[procName].FindIndex(ne => ne.expr.ToString() == atom.ToString());
- if (index == -1)
- {
- index = PrePreds[procName].FindIndex(ne => Expr.Not(ne.expr).ToString() == atom.ToString());
- if (index == -1)
- {
- if(CommandLineOptions.Clo.Trace) Console.WriteLine("Failed to parse {0} (ignoring)", atom);
- return null;
- }
- else
- {
- neg.Add(index);
- }
- }
- else
- {
- pos.Add(index);
- }
- }
-
- var conj = new PredicateAbsConjunct(pos, neg, procName);
- var conjls = new List<PredicateAbsConjunct>();
- conjls.Add(conj);
-
- return Tuple.Create(new PredicateAbsDisjunct(conjls, procName), postExpr);
- }
-
- // blah && blah ==> {blah, blah}
- static IEnumerable<Expr> DecomposeOuterAnd(Expr expr)
- {
- var ret = new List<Expr>();
-
- var nexpr = expr as NAryExpr;
- if (nexpr == null
- || !(nexpr.Fun is BinaryOperator)
- || (nexpr.Fun as BinaryOperator).Op != BinaryOperator.Opcode.And)
- {
- ret.Add(expr);
- }
- else
- {
- foreach (Expr a in nexpr.Args)
- ret.AddRange(DecomposeOuterAnd(a));
- }
-
- return ret;
- }
-
-
- private Model.Element Eval(Expr expr, Dictionary<string, Model.Element> state)
- {
- if (expr is LiteralExpr)
- {
- return ToElem((expr as LiteralExpr).Val);
- }
-
- if (expr is IdentifierExpr)
- {
- return LookupVariable((expr as IdentifierExpr).Name, state, false);
- }
-
- if (expr is OldExpr)
- {
- var ide = (expr as OldExpr).Expr as IdentifierExpr;
- Debug.Assert(ide != null);
-
- return LookupVariable(ide.Name, state, true);
- }
-
- if (expr is NAryExpr)
- {
- var nary = expr as NAryExpr;
- if (nary.Fun is UnaryOperator)
- {
- return ToElem((nary.Fun as UnaryOperator).Evaluate(ToValue(Eval(nary.Args[0], state))));
- }
- if (nary.Fun is BinaryOperator)
- {
- return ToElem((nary.Fun as BinaryOperator).Evaluate(ToValue(Eval(nary.Args[0], state)), ToValue(Eval(nary.Args[1], state))));
- }
- if (nary.Fun is MapSelect && nary.Args.Count == 2)
- {
- var index = Eval(nary.Args[1], state);
- var map = Eval(nary.Args[0], state) as Model.Array;
- Debug.Assert(map != null, "Variable of map type must have an Array-typed value");
- var ret = map.Value.TryEval(index as Model.Element);
- if (ret == null) ret = map.Value.Else;
- Debug.Assert(ret != null);
- return ret;
- }
- Debug.Assert(false, "No other op is handled");
- }
- throw new NotImplementedException(string.Format("Expr of type {0} is not handled", expr.GetType().ToString()));
- }
-
- private Model.Element LookupVariable(string v, Dictionary<string, Model.Element> state, bool tryOld)
- {
- if (tryOld)
- {
- var oldv = string.Format("old({0})", v);
- if (state.ContainsKey(oldv))
- {
- return state[oldv];
- }
- throw new AbsHoudiniInternalError("Cannot handle this case");
- }
-
- if (state.ContainsKey(v))
- {
- return state[v];
- }
-
- /*
- if (boolConstants.Contains(v))
- {
- // value of this constant is immaterial, return true
- return model.MkElement("true");
- }
- */
-
- throw new AbsHoudiniInternalError("Cannot handle this case");
- }
-
- private static VCExpr ToVcVar(string v, Dictionary<string, VCExpr> incarnations, bool tryOld)
- {
- if (tryOld)
- {
- var oldv = string.Format("old({0})", v);
- if (incarnations.ContainsKey(oldv))
- {
- return incarnations[oldv];
- }
- throw new AbsHoudiniInternalError("Cannot handle this case");
- }
-
- if (incarnations.ContainsKey(v))
- {
- return incarnations[v];
- }
-
- throw new AbsHoudiniInternalError("Cannot handle this case");
- }
-
- public static void FindUnsatPairs(VCExpressionGenerator gen, ProverInterface prover)
- {
- unsatPrePredPairs = new HashSet<Tuple<string, int, int, bool, bool>>();
- unsatPostPredPairs = new HashSet<Tuple<string, int, int, bool, bool>>();
-
- var cachePos = new HashSet<Tuple<string, string>>();
- var cacheNeg = new HashSet<Tuple<string, string>>();
- var record = new Action<object, string, int, int, bool, bool>(
- (map, proc, e1, e2, p1, p2) => {
- var key = Tuple.Create(proc, e1, e2, p1, p2);
- if (map == PrePreds)
- unsatPrePredPairs.Add(key);
- else
- unsatPostPredPairs.Add(key);
- }
- );
-
- var predMaps = new List<Dictionary<string, List<NamedExpr>>>();
- predMaps.Add(PrePreds); predMaps.Add(PostPreds);
-
- foreach (var map in predMaps)
- {
- foreach (var proc in map.Keys)
- {
- for (int i = 0; i < 2 * map[proc].Count(); i++)
- {
- var p1 = (i % 2); // polarity
- var e1 = map[proc][i / 2].expr;
- if (p1 == 0) e1 = Expr.Not(e1);
-
- for (int j = 2 * ((i / 2) + 1); j < 2 * map[proc].Count(); j++)
- {
- var p2 = (j % 2); // polarity
- var e2 = map[proc][j / 2].expr;
- if (p2 == 0) e2 = Expr.Not(e2);
-
- var key = Tuple.Create(e1.ToString(), e2.ToString());
- if (cachePos.Contains(key))
- {
- record(map, proc, i / 2, j / 2, p1 == 1, p2 == 1);
- continue;
- }
- else if (cacheNeg.Contains(key))
- {
- continue;
- }
-
- if (!CheckIfUnsat(e1, e2, gen, prover))
- {
- cacheNeg.Add(key);
- continue;
- }
- cachePos.Add(key);
- record(map, proc, i / 2, j / 2, p1 == 1, p2 == 1);
-
- if (CommandLineOptions.Clo.Trace)
- Console.WriteLine("Proved UNSAT: {0} {1}", e1, e2);
- }
- }
- }
- }
- }
-
- // Is a ^ b UNSAT?
- private static bool CheckIfUnsat(Expr a, Expr b, VCExpressionGenerator gen, ProverInterface prover)
- {
- var gatherLitA = new GatherLiterals();
- var gatherLitB = new GatherLiterals();
-
- gatherLitA.Visit(a);
- gatherLitB.Visit(b);
-
- var seta = new HashSet<Variable>();
- var setb = new HashSet<Variable>();
- gatherLitA.literals.Iter(tup => seta.Add(tup.Item1));
- gatherLitB.literals.Iter(tup => setb.Add(tup.Item1));
- seta.IntersectWith(setb);
- if (!seta.Any()) return false;
-
- // Create fresh variables
- return CheckIfUnsat(Expr.And(a, b), gen, prover);
- }
-
- // Is a UNSAT?
- private static bool CheckIfUnsat(Expr a, VCExpressionGenerator gen, ProverInterface prover)
- {
- var gatherLitA = new GatherLiterals();
- gatherLitA.Visit(a);
-
- // Create fresh variables
- var counter = 0;
- var incarnations = new Dictionary<string, VCExpr>();
- foreach (var literal in gatherLitA.literals)
- {
- if (incarnations.ContainsKey(literal.Item2.ToString()))
- continue;
-
- //if(!literal.Item1.TypedIdent.Type.IsInt && !literal.Item1.TypedIdent.Type.IsBool)
- var v = gen.Variable("UNSATCheck" + counter, literal.Item1.TypedIdent.Type);
- incarnations.Add(literal.Item2.ToString(), v);
- counter++;
- }
-
- var vc1 = ToVcExpr(a, incarnations, gen);
- var vc = gen.LabelPos("Temp", vc1);
-
- // check
- prover.AssertAxioms();
- prover.Push();
- prover.Assert(vc, true);
- prover.Check();
- var outcome = prover.CheckOutcomeCore(new AbstractHoudiniErrorReporter());
- prover.Pop();
-
- if (outcome == ProverInterface.Outcome.Valid)
- return true;
- return false;
- }
-
-
- class GatherLiterals : ReadOnlyVisitor
- {
- public List<Tuple<Variable, Expr>> literals;
- bool inOld;
-
- public GatherLiterals()
- {
- literals = new List<Tuple<Variable, Expr>>();
- inOld = false;
- }
-
- public override Expr VisitOldExpr(OldExpr node)
- {
- var prev = inOld;
- inOld = true;
- var ret = base.VisitOldExpr(node);
- inOld = prev;
- return ret;
- }
-
- public override Expr VisitIdentifierExpr(IdentifierExpr node)
- {
- if (inOld)
- literals.Add(Tuple.Create(node.Decl, new OldExpr(Token.NoToken, node) as Expr));
- else
- literals.Add(Tuple.Create(node.Decl, node as Expr));
-
- return node;
- }
- }
-
- private object ToValue(Model.Element elem)
- {
- if (elem is Model.Integer)
- {
- return Microsoft.Basetypes.BigNum.FromInt((elem as Model.Integer).AsInt());
- }
- if (elem is Model.Boolean)
- {
- return (elem as Model.Boolean).Value;
- }
- if (elem is Model.DatatypeValue && (elem as Model.DatatypeValue).Arguments.Length == 1 &&
- (elem as Model.DatatypeValue).ConstructorName == "-" &&
- (elem as Model.DatatypeValue).Arguments[0] is Model.Integer)
- {
- // negative number as "-" @ int
- return Microsoft.Basetypes.BigNum.FromInt(-1 * ((elem as Model.DatatypeValue).Arguments[0] as Model.Integer).AsInt());
- }
- throw new NotImplementedException("Cannot yet handle this Model.Element type");
- }
-
- private Model.Element ToElem(object val)
- {
- if (val is bool || val is int || val is Basetypes.BigNum)
- return model.MkElement(val.ToString());
- throw new NotImplementedException("Cannot yet handle this value type");
- }
-
- // replace v by old(v)
- private static Expr MakeOld(Expr expr)
- {
- var substalways = new Substitution(v => new OldExpr(Token.NoToken, Expr.Ident(v)));
- var substold = new Substitution(v => Expr.Ident(v));
-
- return Substituter.ApplyReplacingOldExprs(substalways, substold, expr);
- }
-
- private static Expr ToExpr(Expr expr, Func<string, Expr> always, Func<string, Expr> forold)
- {
- var substalways = new Substitution(v =>
- {
- var ret = always(v.Name);
- if (ret != null) return ret;
- else return Expr.Ident(v);
- });
- var substold = new Substitution(v =>
- {
- var ret = forold(v.Name);
- if (ret != null) return ret;
- else return new OldExpr(Token.NoToken, Expr.Ident(v));
- });
-
- return Substituter.ApplyReplacingOldExprs(substalways, substold, expr);
- }
-
- private static VCExpr ToVcExpr(Expr expr, Dictionary<string, VCExpr> incarnations, VCExpressionGenerator gen)
- {
- if (expr is LiteralExpr)
- {
- var val = (expr as LiteralExpr).Val;
- if (val is bool)
- {
- if ((bool)val)
- {
- return VCExpressionGenerator.True;
- }
- else
- {
- return VCExpressionGenerator.False;
- }
- }
- else if (val is Microsoft.Basetypes.BigNum)
- {
- return gen.Integer((Microsoft.Basetypes.BigNum)val);
- }
-
- throw new NotImplementedException("Cannot handle literals of this type");
- }
-
- if (expr is IdentifierExpr)
- {
- return ToVcVar((expr as IdentifierExpr).Name, incarnations, false);
- }
-
- if (expr is OldExpr)
- {
- var ide = (expr as OldExpr).Expr as IdentifierExpr;
- Debug.Assert(ide != null);
-
- return ToVcVar(ide.Name, incarnations, true);
- }
-
- if (expr is NAryExpr)
- {
- var nary = expr as NAryExpr;
- if (nary.Fun is UnaryOperator)
- {
- if ((nary.Fun as UnaryOperator).Op == UnaryOperator.Opcode.Not)
- return gen.Not(ToVcExpr(nary.Args[0], incarnations, gen));
- else if ((nary.Fun as UnaryOperator).Op == UnaryOperator.Opcode.Neg)
- return gen.Function(VCExpressionGenerator.SubIOp, gen.Integer(Basetypes.BigNum.FromInt(0)), ToVcExpr(nary.Args[0], incarnations, gen));
- else
- Debug.Assert(false, "No other unary op is handled");
- }
- if (nary.Fun is BinaryOperator)
- {
- return gen.Function(Translate(nary.Fun as BinaryOperator), ToVcExpr(nary.Args[0], incarnations, gen), ToVcExpr(nary.Args[1], incarnations, gen));
- }
- if (nary.Fun is MapSelect && nary.Args.Count == 2)
- {
- return gen.Select(ToVcExpr(nary.Args[0], incarnations, gen), ToVcExpr(nary.Args[1], incarnations, gen));
- }
- Debug.Assert(false, "No other op is handled");
- }
- throw new NotImplementedException(string.Format("Expr of type {0} is not handled", expr.GetType().ToString()));
- }
-
- private static VCExprOp Translate(BinaryOperator op)
- {
- switch (op.Op)
- {
- case BinaryOperator.Opcode.Add:
- return VCExpressionGenerator.AddIOp;
- case BinaryOperator.Opcode.Sub:
- return VCExpressionGenerator.SubIOp;
- case BinaryOperator.Opcode.Mul:
- return VCExpressionGenerator.MulIOp;
- case BinaryOperator.Opcode.Div:
- return VCExpressionGenerator.DivIOp;
- case BinaryOperator.Opcode.Mod:
- return VCExpressionGenerator.ModOp;
- case BinaryOperator.Opcode.Eq:
- case BinaryOperator.Opcode.Iff:
- // we don't distinguish between equality and equivalence at this point
- return VCExpressionGenerator.EqOp;
- case BinaryOperator.Opcode.Neq:
- return VCExpressionGenerator.NeqOp;
- case BinaryOperator.Opcode.Lt:
- return VCExpressionGenerator.LtOp;
- case BinaryOperator.Opcode.Le:
- return VCExpressionGenerator.LeOp;
- case BinaryOperator.Opcode.Ge:
- return VCExpressionGenerator.GeOp;
- case BinaryOperator.Opcode.Gt:
- return VCExpressionGenerator.GtOp;
- case BinaryOperator.Opcode.Imp:
- return VCExpressionGenerator.ImpliesOp;
- case BinaryOperator.Opcode.And:
- return VCExpressionGenerator.AndOp;
- case BinaryOperator.Opcode.Or:
- return VCExpressionGenerator.OrOp;
- case BinaryOperator.Opcode.Subtype:
- return VCExpressionGenerator.SubtypeOp;
- default:
- Contract.Assert(false);
- throw new NotImplementedException();
- }
-
- }
-
- // If "false" is a post-predicate, then remove its "pre" constraint from all others, whenever possible
- public void Simplify()
- {
- // find "false"
- var findex = PostPreds[procName].FindIndex(ne => (ne.expr is LiteralExpr) && (ne.expr as LiteralExpr).IsFalse);
- if (findex < 0) return;
- if (value[findex].isTrue)
- {
- // procedure doesn't return
- for (int i = 0; i < value.Length; i++)
- if (i != findex) value[i] = new PredicateAbsDisjunct(false, procName);
- return;
- }
- if (value[findex].isFalse)
- return;
-
- for (int i = 0; i < value.Length; i++)
- if (i != findex) value[i].Subtract(value[findex]);
- }
-
- public HashSet<string> GetPredicates(Program program, VCExpressionGenerator gen, ProverInterface prover)
- {
- var ret = new HashSet<string>();
- if (isFalse) return ret;
- Simplify();
-
- // Find the free expressions
- var proc = program.Procedures.FirstOrDefault(p => p.Name == procName);
- Contract.Assert(proc != null);
- Expr freeSummary = Expr.True;
- foreach (var req in proc.Requires.OfType<Requires>().Where(req => req.Free))
- {
- freeSummary = Expr.And(freeSummary, MakeOld(req.Condition));
- }
- foreach (var ens in proc.Ensures.OfType<Ensures>().Where(ens => ens.Free))
- {
- freeSummary = Expr.And(freeSummary, ens.Condition);
- }
-
- for (int i = 0; i < PostPreds[procName].Count; i++)
- {
- if (value[i].isFalse) continue;
- if (PostPreds[procName][i].expr is LiteralExpr && (PostPreds[procName][i].expr as LiteralExpr).IsFalse)
- continue;
-
- if (value[i].isTrue)
- ret.Add(PostPreds[procName][i].expr.ToString());
- else
- {
- foreach (var c in value[i].GetConjuncts())
- {
- var s = Expr.Imp(c.ToExpr(j => PrePreds[procName][j].expr), PostPreds[procName][i].expr);
- if (CheckIfUnsat(Expr.And(freeSummary, Expr.Not(s)), gen, prover))
- continue;
- ret.Add(s.ToString());
- }
- }
- }
- return ret;
- }
-
- public override string ToString()
- {
- var ret = "";
- if (isFalse) return "false";
- var first = true;
-
- for(int i = 0; i < PostPreds[procName].Count; i++)
- {
- if(value[i].isFalse) continue;
-
- if(value[i].isTrue)
- ret += string.Format("{0}{1}", first ? "" : " && ", PostPreds[procName][i]);
- else
- ret += string.Format("{0}({1} ==> {2})", first ? "" : " && ", value[i], PostPreds[procName][i]);
-
- first = false;
- }
- if (ret == "") ret = "true";
- return ret;
- }
-
-
- #region ISummaryElement Members
-
- public ISummaryElement Copy()
- {
- var ret = new PredicateAbs(procName);
- ret.isFalse = isFalse;
- ret.value = new PredicateAbsDisjunct[value.Length];
- for (int i = 0; i < value.Length; i++)
- ret.value[i] = value[i];
- return ret;
- }
-
- public ISummaryElement GetFlaseSummary(Program program, Implementation impl)
- {
- return new PredicateAbs(impl.Name);
- }
-
- public ISummaryElement GetTrueSummary(Program program, Implementation impl)
- {
- var ret = new PredicateAbs(impl.Name);
- ret.isFalse = false;
- for (int i = 0; i < PostPreds[this.procName].Count; i++) ret.value[i] = new PredicateAbsDisjunct(false, impl.Name);
-
- return ret;
- }
-
- public void Join(Dictionary<string, Model.Element> state, Model model)
- {
- PredicateAbs.model = model;
-
- // Evaluate each predicate on the state
- var prePredsVal = new bool[PrePreds[procName].Count];
- var postPredsVal = new bool[PostPreds[procName].Count];
-
- var indexSeq = new List<int>();
- for (int i = 0; i < PrePreds[procName].Count; i++) indexSeq.Add(i);
-
- for (int i = 0; i < PrePreds[procName].Count; i++)
- {
- var v = ToValue(Eval(PrePreds[procName][i].expr, state));
- Debug.Assert(v is bool);
- prePredsVal[i] = (bool)v;
- }
-
- for (int i = 0; i < PostPreds[procName].Count; i++)
- {
- var v = ToValue(Eval(PostPreds[procName][i].expr, state));
- Debug.Assert(v is bool);
- postPredsVal[i] = (bool)v;
- }
-
- for (int i = 0; i < PostPreds[procName].Count; i++)
- {
- // No hope for this post pred?
- if (!isFalse && value[i].isFalse) continue;
-
- var newDisj = new PredicateAbsDisjunct(true, procName);
- if (!postPredsVal[i])
- {
- newDisj = new PredicateAbsDisjunct(indexSeq.Where(j => !prePredsVal[j]), indexSeq.Where(j => prePredsVal[j] && !PosPrePreds[procName].Contains(j)), procName);
- }
-
- if (isFalse)
- value[i] = newDisj;
- else
- value[i] = PredicateAbsDisjunct.And(value[i], newDisj);
- }
-
- /*
- // do beta(model)
- var that = new PredicateAbsDisjunct[PostPreds[procName].Count];
- for (int i = 0; i < PostPreds[procName].Count; i++)
- {
- if (postPredsVal[i])
- that[i] = new PredicateAbsDisjunct(true, procName);
- else if (i == 0)
- {
- Debug.Assert(PostPreds[procName][0].ToString() == "false");
- var newDisj = new PredicateAbsDisjunct(true, procName);
- newDisj = new PredicateAbsDisjunct(indexSeq.Where(j => !prePredsVal[j]), indexSeq.Where(j => prePredsVal[j]), procName);
- that[i] = newDisj;
- }
- else
- {
- // false
- that[i] = new PredicateAbsDisjunct(false, procName);
- }
- }
-
- // Do join
- for (int i = 0; i < PostPreds[procName].Count; i++)
- {
- if (isFalse)
- value[i] = that[i];
- else
- {
- if (i == 0)
- value[i] = PredicateAbsDisjunct.And(value[i], that[i]);
- else
- {
- var c1 = PredicateAbsDisjunct.And(value[i], that[i]);
- var c2 = PredicateAbsDisjunct.And(value[i], that[0]);
- var c3 = PredicateAbsDisjunct.And(value[0], that[i]);
- value[i] = PredicateAbsDisjunct.Or(c1, c2);
- value[i] = PredicateAbsDisjunct.Or(value[i], c3);
- }
- }
- }
- */
- isFalse = false;
-
- //Console.WriteLine("Result of Join: {0}", this.ToString());
- }
-
- // Precondition: the upper guys are just {true/false/upper-candidate} ==> post-pred
- public void Meet(ISummaryElement iother)
- {
- var other = iother as PredicateAbs;
- if (isFalse) return;
- if (other.isFalse)
- {
- isFalse = true;
- for (int i = 0; i < PostPreds[this.procName].Count; i++) value[i] = null;
- return;
- }
- Debug.Assert(this.procName == other.procName);
-
- for (int i = 0; i < PostPreds[this.procName].Count; i++)
- {
- value[i] = PredicateAbsDisjunct.Or(value[i], other.value[i]);
- }
- }
-
- public bool IsEqual(ISummaryElement other)
- {
- var that = other as PredicateAbs;
- if (isFalse && that.isFalse) return true;
- if (isFalse || that.isFalse) return false;
- for (int i = 0; i < PostPreds[procName].Count; i++)
- {
- if (!PredicateAbsDisjunct.syntacticLessThan(value[i], that.value[i]) ||
- !PredicateAbsDisjunct.syntacticLessThan(that.value[i], value[i]))
- return false;
- }
- return true;
- }
-
- // Precondition: the upper guys are just {true/false/upper-candidate} ==> post-pred
- // Postcondition: the returned value is also of this form (for just one post-pred)
- public ISummaryElement AbstractConsequence(ISummaryElement iupper)
- {
- var upper = iupper as PredicateAbs;
-
- for (int i = 0; i < PostPreds[this.procName].Count; i++)
- {
- if (upper.value[i].isTrue) continue;
- if (!UpperCandidates.ContainsKey(Tuple.Create(procName, i))) continue;
-
- foreach (var candidate in UpperCandidates[Tuple.Create(procName, i)])
- {
- if (PredicateAbsDisjunct.syntacticLessThan(candidate, upper.value[i]))
- continue;
- if (!this.isFalse && !PredicateAbsDisjunct.syntacticLessThan(candidate, this.value[i]))
- continue;
-
- var ret = new PredicateAbs(this.procName);
- ret.isFalse = false;
- for (int j = 0; j < PostPreds[this.procName].Count; j++)
- ret.value[j] = new PredicateAbsDisjunct(false, this.procName);
-
- ret.value[i] = candidate;
-
- return ret;
- }
- }
-
- // Giveup: the abstract consequence is too difficult to compute
- return null;
- }
-
- public VCExpr GetSummaryExpr(Dictionary<string, VCExpr> incarnations, VCExpressionGenerator gen)
- {
- if (isFalse)
- return VCExpressionGenerator.False;
-
- var ret = VCExpressionGenerator.True;
-
- for(int i = 0; i < PostPreds[procName].Count; i++)
- {
- ret = gen.AndSimp(ret, gen.ImpliesSimp(value[i].ToVcExpr(j => ToVcExpr(PrePreds[procName][j].expr, incarnations, gen), gen), ToVcExpr(PostPreds[procName][i].expr, incarnations, gen)));
- }
-
- return ret;
- }
-
- public Expr GetSummaryExpr(Func<string, Expr> always, Func<string, Expr> forold)
- {
- if (isFalse)
- return Expr.False;
-
- Expr ret = Expr.True;
-
- for (int i = 0; i < PostPreds[procName].Count; i++)
- {
- ret = Expr.And(ret, Expr.Imp(value[i].ToExpr(j => ToExpr(PrePreds[procName][j].expr, always, forold)), ToExpr(PostPreds[procName][i].expr, always, forold)));
- }
-
- return ret;
- }
-
- #endregion
- }
-
- public class PredicateAbsDisjunct
- {
- List<PredicateAbsConjunct> conjuncts;
- string ProcName;
- public bool isTrue {get; private set;}
- public bool isFalse
- {
- get
- {
- if (isTrue) return false;
- return conjuncts.Count == 0;
- }
- }
-
- public PredicateAbsDisjunct(bool isTrue, string ProcName)
- {
- this.isTrue = isTrue;
- this.ProcName = ProcName;
- conjuncts = new List<PredicateAbsConjunct>();
- }
-
- public PredicateAbsDisjunct(List<PredicateAbsConjunct> conjuncts, string ProcName)
- {
- isTrue = false;
- this.conjuncts = conjuncts;
- this.ProcName = ProcName;
- }
-
- // Disjunct of singleton conjuncts
- public PredicateAbsDisjunct(IEnumerable<int> pos, IEnumerable<int> neg, string ProcName)
- {
- this.ProcName = ProcName;
- conjuncts = new List<PredicateAbsConjunct>();
- isTrue = false;
- pos.Iter(p => conjuncts.Add(PredicateAbsConjunct.Singleton(p, true, ProcName)));
- neg.Iter(p => conjuncts.Add(PredicateAbsConjunct.Singleton(p, false, ProcName)));
- }
-
- // Does d1 describe a smaller set of states than d2? This is true when every conjunct of d1
- // is smaller than some conjunct of d2
- public static bool syntacticLessThan(PredicateAbsDisjunct d1, PredicateAbsDisjunct d2)
- {
- if (d2.isTrue) return true;
- if (d1.isTrue) return false;
- if (d1.isFalse) return true;
- if (d2.isFalse) return false;
-
- foreach (var c1 in d1.conjuncts)
- {
- if (d2.conjuncts.Any(c2 => PredicateAbsConjunct.syntacticLessThan(c1, c2)))
- continue;
- else
- return false;
- }
- return true;
- }
-
- public static PredicateAbsDisjunct And(PredicateAbsDisjunct v1, PredicateAbsDisjunct v2)
- {
- if (v1.isTrue) return v2;
- if (v2.isTrue) return v1;
-
- var result = new List<PredicateAbsConjunct>();
-
- foreach (var c1 in v1.conjuncts)
- {
- foreach (var c2 in v2.conjuncts)
- {
- var c = PredicateAbsConjunct.And(c1, c2);
- if (c.isFalse) continue;
- if (result.Any(cprime => c.implies(cprime))) continue;
- var tmp = new List<PredicateAbsConjunct>();
- tmp.Add(c);
- result.Where(cprime => !cprime.implies(c)).Iter(cprime => tmp.Add(cprime));
- result = tmp;
- }
- }
-
- return new PredicateAbsDisjunct(result, v1.ProcName);
- }
-
- public static PredicateAbsDisjunct Or(PredicateAbsDisjunct v1, PredicateAbsDisjunct v2)
- {
- if (v1.isTrue) return v1;
- if (v2.isTrue) return v2;
- if (v1.isFalse) return v2;
- if (v2.isFalse) return v1;
-
- var result = new List<PredicateAbsConjunct>();
- v1.conjuncts.Iter(c => result.Add(c));
-
- foreach (var c in v2.conjuncts)
- {
- if (result.Any(cprime => c.implies(cprime))) continue;
- var tmp = new List<PredicateAbsConjunct>();
- tmp.Add(c);
- result.Where(cprime => !cprime.implies(c)).Iter(cprime => tmp.Add(cprime));
- result = tmp;
- }
-
- return new PredicateAbsDisjunct(result, v1.ProcName);
- }
-
- public VCExpr ToVcExpr(Func<int, VCExpr> predToExpr, VCExpressionGenerator gen)
- {
- if (isTrue) return VCExpressionGenerator.True;
- var ret = VCExpressionGenerator.False;
- conjuncts.Iter(c => ret = gen.OrSimp(ret, c.ToVcExpr(predToExpr, gen)));
- return ret;
- }
-
- public Expr ToExpr(Func<int, Expr> predToExpr)
- {
- if (isTrue) return Expr.True;
- Expr ret = Expr.False;
- conjuncts.Iter(c => ret = Expr.Or(ret, c.ToExpr(predToExpr)));
- return ret;
- }
-
- public override string ToString()
- {
- if(isTrue)
- return "true";
- var ret = "";
- var first = true;
- foreach (var c in conjuncts)
- {
- if (c.isFalse) continue;
- ret += string.Format("{0}{1}", first ? "" : " || ", c);
- first = false;
- }
- return ret;
- }
-
- public void Subtract(PredicateAbsDisjunct that)
- {
- var ncon = new List<PredicateAbsConjunct>();
- foreach (var c1 in conjuncts)
- {
- if (that.conjuncts.Any(c2 => c1.implies(c2)))
- continue;
- ncon.Add(c1);
- }
- conjuncts = ncon;
- }
-
- public IEnumerable<PredicateAbsConjunct> GetConjuncts()
- {
- return conjuncts;
- }
-
- }
-
- public class PredicateAbsConjunct
- {
- static int ConjunctBound = 3;
-
- public bool isFalse { get; private set; }
- HashSet<int> posPreds;
- HashSet<int> negPreds;
- string ProcName;
-
- public static void Initialize(int bound)
- {
- ConjunctBound = bound;
- }
-
- private void Normalize()
- {
- if (posPreds.Intersect(negPreds).Any() || negPreds.Intersect(posPreds).Any() || (posPreds.Count + negPreds.Count > ConjunctBound))
- {
- isFalse = true;
- posPreds = new HashSet<int>();
- negPreds = new HashSet<int>();
- }
- }
-
- // Do this step only once in a while?
- private void StrongNormalize()
- {
- if (isFalse) return;
-
- var candidates = new List<Tuple<int, bool>>();
- posPreds.Iter(p => candidates.Add(Tuple.Create(p, true)));
- negPreds.Iter(p => candidates.Add(Tuple.Create(p, false)));
- var drop = new HashSet<int>();
- for (int i = 0; i < candidates.Count; i++)
- {
- for (int j = 0; j < candidates.Count; j++)
- {
- if (i == j) continue;
-
- var key = Tuple.Create(ProcName, candidates[i].Item1, candidates[j].Item1,
- candidates[i].Item2, candidates[j].Item2);
- if (PredicateAbs.unsatPrePredPairs.Contains(key))
- {
- isFalse = true;
- posPreds = new HashSet<int>();
- negPreds = new HashSet<int>();
- return;
- }
-
- key = Tuple.Create(ProcName, candidates[i].Item1, candidates[j].Item1,
- candidates[i].Item2, !candidates[j].Item2);
-
- if (PredicateAbs.unsatPrePredPairs.Contains(key))
- drop.Add(candidates[j].Item1);
- }
- }
-
- posPreds.ExceptWith(drop);
- negPreds.ExceptWith(drop);
- }
-
- public PredicateAbsConjunct(bool isFalse, string ProcName)
- {
- posPreds = new HashSet<int>();
- negPreds = new HashSet<int>();
- this.isFalse = isFalse;
- this.ProcName = ProcName;
- }
-
- // do we know that c1 is surely less than or equal to c2? That is, c1 describes a smaller
- // concretization. We check that c2 is a sub-conjunct of c1
- public static bool syntacticLessThan(PredicateAbsConjunct c1, PredicateAbsConjunct c2)
- {
- if (c1.isFalse) return true;
- if (c2.isFalse) return false;
- return (c2.posPreds.IsSubsetOf(c1.posPreds) && c2.negPreds.IsSubsetOf(c1.negPreds));
- }
-
- public static PredicateAbsConjunct Singleton(int v, bool isPositive, string ProcName)
- {
- if (isPositive)
- return new PredicateAbsConjunct(new int[] { v }, new HashSet<int>(), ProcName);
- else
- return new PredicateAbsConjunct(new HashSet<int>(), new int[] { v }, ProcName);
- }
-
- public PredicateAbsConjunct(IEnumerable<int> pos, IEnumerable<int> neg, string ProcName)
- {
- isFalse = false;
- posPreds = new HashSet<int>(pos);
- negPreds = new HashSet<int>(neg);
- this.ProcName = ProcName;
- Normalize();
- }
-
- public static PredicateAbsConjunct And(PredicateAbsConjunct v1, PredicateAbsConjunct v2)
- {
- if (v1.isFalse || v2.isFalse) return new PredicateAbsConjunct(true, v1.ProcName);
- var ret = new PredicateAbsConjunct(v1.posPreds.Union(v2.posPreds), v1.negPreds.Union(v2.negPreds), v1.ProcName);
- ret.StrongNormalize();
- return ret;
- }
-
- public bool implies(PredicateAbsConjunct v)
- {
- if (isFalse) return true;
- if (v.isFalse) return false;
- return (posPreds.IsSupersetOf(v.posPreds) && negPreds.IsSupersetOf(v.negPreds));
- }
-
- public VCExpr ToVcExpr(Func<int, VCExpr> predToExpr, VCExpressionGenerator gen)
- {
- if (isFalse) return VCExpressionGenerator.False;
- var ret = VCExpressionGenerator.True;
- posPreds.Iter(p => ret = gen.AndSimp(ret, predToExpr(p)));
- negPreds.Iter(p => ret = gen.AndSimp(ret, gen.Not(predToExpr(p))));
- return ret;
- }
-
- public Expr ToExpr(Func<int, Expr> predToExpr)
- {
- if (isFalse) return Expr.False;
- Expr ret = Expr.True;
- var pp = posPreds.ToList(); pp.Sort();
- var np = negPreds.ToList(); np.Sort();
- pp.Iter(p => ret = Expr.And(ret, predToExpr(p)));
- np.Iter(p => ret = Expr.And(ret, Expr.Not(predToExpr(p))));
- return ret;
- }
-
- public override string ToString()
- {
- if (isFalse)
- return "false";
-
- var ret = "";
- var first = true;
- foreach (var p in posPreds)
- {
- ret += string.Format("{0}{1}", first ? "" : " && ", PredicateAbs.PrePreds[ProcName][p]);
- first = false;
- }
- foreach (var p in negPreds)
- {
- ret += string.Format("{0}!{1}", first ? "" : " && ", PredicateAbs.PrePreds[ProcName][p]);
- first = false;
- }
- return ret;
- }
- }
-
- class FindSummaryPred : MutatingVCExprVisitor<bool>
- {
- public List<Tuple<string, bool, VCExprVar, VCExprNAry>> summaryPreds;
- int assertId;
- private static int CounterId = 0;
-
- public FindSummaryPred(VCExpressionGenerator gen, int assertId)
- : base(gen)
- {
- summaryPreds = new List<Tuple<string, bool, VCExprVar, VCExprNAry>>();
- this.assertId = assertId;
- }
-
- protected override VCExpr/*!*/ UpdateModifiedNode(VCExprNAry/*!*/ originalNode,
- List<VCExpr/*!*/>/*!*/ newSubExprs,
- // has any of the subexpressions changed?
- bool changed,
- bool arg)
- {
- Contract.Ensures(Contract.Result<VCExpr>() != null);
-
- VCExpr ret;
- if (changed)
- ret = Gen.Function(originalNode.Op,
- newSubExprs, originalNode.TypeArguments);
- else
- ret = originalNode;
-
- VCExprNAry retnary = ret as VCExprNAry;
- if (retnary == null) return ret;
- var op = retnary.Op as VCExprBoogieFunctionOp;
-
- if (op == null)
- {
- var lop = retnary.Op as VCExprLabelOp;
- if (lop == null) return ret;
- if (lop.pos) return ret;
- if (!lop.label.Equals("@" + assertId.ToString())) return ret;
-
- //var subexpr = retnary[0] as VCExprNAry;
- //if (subexpr == null) return ret;
- //op = subexpr.Op as VCExprBoogieFunctionOp;
- //if (op == null) return ret;
-
- var subexpr = retnary[0] as VCExprVar;
- if (subexpr == null) return ret;
- if (!subexpr.Name.StartsWith("AbstractHoudiniControl")) return ret;
-
- for (int i = 0; i < summaryPreds.Count; i++)
- {
- if (summaryPreds[i].Item3 == subexpr)
- summaryPreds[i] = Tuple.Create(summaryPreds[i].Item1, true, summaryPreds[i].Item3, summaryPreds[i].Item4);
- }
- return ret;
- }
-
- string calleeName = op.Func.Name;
-
- if (!calleeName.EndsWith(AbstractHoudini.summaryPredSuffix))
- return ret;
-
- var controlConst = Gen.Variable("AbstractHoudiniControl" + CounterId, Microsoft.Boogie.Type.Bool);
- CounterId++;
-
- summaryPreds.Add(Tuple.Create(calleeName.Substring(0, calleeName.Length - AbstractHoudini.summaryPredSuffix.Length), false, controlConst, retnary));
-
- return controlConst;
- //return ret;
- }
-
- }
-
- class FindExistentialFunctions : MutatingVCExprVisitor<bool>
- {
- public List<Tuple<string, VCExprVar, VCExprNAry>> funcCalls;
- private HashSet<string> existentialFunctions;
- private static int CounterId = 0;
-
- public FindExistentialFunctions(VCExpressionGenerator gen, HashSet<string> existentialFunctions)
- : base(gen)
- {
- funcCalls = new List<Tuple<string, VCExprVar, VCExprNAry>>();
- this.existentialFunctions = existentialFunctions;
- }
-
- protected override VCExpr/*!*/ UpdateModifiedNode(VCExprNAry/*!*/ originalNode,
- List<VCExpr/*!*/>/*!*/ newSubExprs,
- // has any of the subexpressions changed?
- bool changed,
- bool arg)
- {
- Contract.Ensures(Contract.Result<VCExpr>() != null);
-
- VCExpr ret;
- if (changed)
- ret = Gen.Function(originalNode.Op,
- newSubExprs, originalNode.TypeArguments);
- else
- ret = originalNode;
-
- VCExprNAry retnary = ret as VCExprNAry;
- if (retnary == null) return ret;
- var op = retnary.Op as VCExprBoogieFunctionOp;
- if (op == null) return ret;
-
- string calleeName = op.Func.Name;
-
- if (!existentialFunctions.Contains(calleeName))
- return ret;
-
- var controlConst = Gen.Variable("AbsHoudiniControl" + CounterId, Microsoft.Boogie.Type.Bool);
- CounterId++;
-
- funcCalls.Add(Tuple.Create(calleeName, controlConst, retnary));
-
- return controlConst;
- }
-
- }
-
- class AbstractHoudiniErrorReporter : ProverInterface.ErrorHandler
- {
- public Model model;
-
- public AbstractHoudiniErrorReporter()
- {
- model = null;
- }
-
- public override void OnModel(IList<string> labels, Model model, ProverInterface.Outcome proverOutcome)
- {
- Debug.Assert(model != null);
- if(CommandLineOptions.Clo.PrintErrorModel >= 1) model.Write(Console.Out);
- this.model = model;
- }
- }
-
- public class AbsHoudiniInternalError : System.ApplicationException
- {
- public AbsHoudiniInternalError(string msg) : base(msg) { }
-
- };
-
- public class SimpleUtil
- {
- // Constructs a mapping from procedure names to the implementation
- public static Dictionary<string, Implementation> nameImplMapping(Program p)
- {
- var m = new Dictionary<string, Implementation>();
- foreach (var impl in p.Implementations)
- {
- m.Add(impl.Name, impl);
- }
-
- return m;
- }
-
- // is "assert true"?
- public static bool isAssertTrue(Cmd cmd)
- {
- var acmd = cmd as AssertCmd;
- if (acmd == null) return false;
- var le = acmd.Expr as LiteralExpr;
- if (le == null) return false;
- if (le.IsTrue) return true;
- return false;
- }
- }
-
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Boogie;
+using Microsoft.Boogie.VCExprAST;
+using VC;
+using Outcome = VC.VCGen.Outcome;
+using Bpl = Microsoft.Boogie;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using Microsoft.Boogie.GraphUtil;
+
+namespace Microsoft.Boogie.Houdini {
+
+ public class AbsHoudini
+ {
+ Dictionary<string, Function> existentialFunctions;
+ Program program;
+ Dictionary<string, Implementation> name2Impl;
+ Dictionary<string, VCExpr> impl2VC;
+ Dictionary<string, List<Tuple<string, Function, NAryExpr>>> impl2FuncCalls;
+ // constant -> the naryexpr that it replaced
+ Dictionary<string, NAryExpr> constant2FuncCall;
+
+ // function -> its abstract value
+ Dictionary<string, IAbstractDomain> function2Value;
+
+ // impl -> functions assumed/asserted
+ Dictionary<string, HashSet<string>> impl2functionsAsserted, impl2functionsAssumed;
+
+ // funtions -> impls where assumed/asserted
+ Dictionary<string, HashSet<string>> function2implAssumed, function2implAsserted;
+
+ // impl -> handler, collector
+ Dictionary<string, Tuple<ProverInterface.ErrorHandler, AbsHoudiniCounterexampleCollector>> impl2ErrorHandler;
+
+ // Essentials: VCGen, Prover
+ VCGen vcgen;
+ ProverInterface prover;
+
+ // Stats
+ TimeSpan proverTime;
+ int numProverQueries;
+
+ public AbsHoudini(Program program, IAbstractDomain defaultElem)
+ {
+ this.program = program;
+ this.impl2VC = new Dictionary<string, VCExpr>();
+ this.impl2FuncCalls = new Dictionary<string, List<Tuple<string, Function, NAryExpr>>>();
+ this.existentialFunctions = new Dictionary<string, Function>();
+ this.name2Impl = new Dictionary<string, Implementation>();
+ this.impl2functionsAsserted = new Dictionary<string, HashSet<string>>();
+ this.impl2functionsAssumed = new Dictionary<string, HashSet<string>>();
+ this.function2implAsserted = new Dictionary<string, HashSet<string>>();
+ this.function2implAssumed = new Dictionary<string, HashSet<string>>();
+ this.impl2ErrorHandler = new Dictionary<string, Tuple<ProverInterface.ErrorHandler, AbsHoudiniCounterexampleCollector>>();
+ this.constant2FuncCall = new Dictionary<string, NAryExpr>();
+
+ // Find the existential functions
+ foreach (var func in program.Functions
+ .Where(f => QKeyValue.FindBoolAttribute(f.Attributes, "existential")))
+ existentialFunctions.Add(func.Name, func);
+
+ this.function2Value = new Dictionary<string, IAbstractDomain>();
+ foreach (var func in existentialFunctions.Values)
+ {
+ // Find if the function wishes to use a specific abstract domain
+ var domain = QKeyValue.FindStringAttribute(func.Attributes, "absdomain");
+ if (domain == null)
+ {
+ function2Value[func.Name] = defaultElem.Bottom();
+ }
+ else
+ {
+ function2Value[func.Name] = AbstractDomainFactory.GetInstance(domain);
+ }
+ }
+ existentialFunctions.Keys.Iter(f => function2implAssumed.Add(f, new HashSet<string>()));
+ existentialFunctions.Keys.Iter(f => function2implAsserted.Add(f, new HashSet<string>()));
+
+ // type check
+ existentialFunctions.Values.Iter(func =>
+ {
+ if (func.OutParams.Count != 1 || !func.OutParams[0].TypedIdent.Type.IsBool)
+ throw new AbsHoudiniInternalError(string.Format("Existential function {0} must return bool", func.Name));
+ if(func.Body != null)
+ throw new AbsHoudiniInternalError(string.Format("Existential function {0} should not have a body", func.Name));
+ var args = new List<Type>();
+ func.InParams.Iter(v => args.Add(v.TypedIdent.Type));
+ string msg = "";
+ if (!function2Value[func.Name].TypeCheck(args, out msg))
+ throw new AbsHoudiniInternalError("TypeError: " + msg);
+ });
+
+ //if (CommandLineOptions.Clo.ProverKillTime > 0)
+ // CommandLineOptions.Clo.ProverOptions.Add(string.Format("TIME_LIMIT={0}", CommandLineOptions.Clo.ProverKillTime));
+
+ Inline();
+
+ this.vcgen = new VCGen(program, CommandLineOptions.Clo.SimplifyLogFilePath, CommandLineOptions.Clo.SimplifyLogFileAppend, new List<Checker>());
+ this.prover = ProverInterface.CreateProver(program, CommandLineOptions.Clo.SimplifyLogFilePath, CommandLineOptions.Clo.SimplifyLogFileAppend, CommandLineOptions.Clo.ProverKillTime);
+
+ this.proverTime = TimeSpan.Zero;
+ this.numProverQueries = 0;
+
+ program.Implementations
+ .Where(impl => !impl.SkipVerification)
+ .Iter(impl => name2Impl.Add(impl.Name, impl));
+
+ // Let's do VC Gen (and also build dependencies)
+ name2Impl.Values.Iter(GenVC);
+ }
+
+ public VCGenOutcome ComputeSummaries()
+ {
+ var overallOutcome = new VCGenOutcome(ProverInterface.Outcome.Valid, new List<Counterexample>());
+
+ // Compute SCCs and determine a priority order for impls
+ var Succ = new Dictionary<string, HashSet<string>>();
+ var Pred = new Dictionary<string, HashSet<string>>();
+ name2Impl.Keys.Iter(s => Succ[s] = new HashSet<string>());
+ name2Impl.Keys.Iter(s => Pred[s] = new HashSet<string>());
+
+ foreach(var impl in name2Impl.Keys) {
+ Succ[impl] = new HashSet<string>();
+ impl2functionsAsserted[impl].Iter(f =>
+ function2implAssumed[f].Iter(succ =>
+ {
+ Succ[impl].Add(succ);
+ Pred[succ].Add(impl);
+ }));
+ }
+
+ var sccs = new StronglyConnectedComponents<string>(name2Impl.Keys,
+ new Adjacency<string>(n => Pred[n]),
+ new Adjacency<string>(n => Succ[n]));
+ sccs.Compute();
+
+ // impl -> priority
+ var impl2Priority = new Dictionary<string, int>();
+ int p = 0;
+ foreach (var scc in sccs)
+ {
+ foreach (var impl in scc)
+ {
+ impl2Priority.Add(impl, p);
+ p++;
+ }
+ }
+
+ var worklist = new SortedSet<Tuple<int, string>>();
+ name2Impl.Keys.Iter(k => worklist.Add(Tuple.Create(impl2Priority[k], k)));
+
+ while (worklist.Any())
+ {
+ var impl = worklist.First().Item2;
+ worklist.Remove(worklist.First());
+
+ var gen = prover.VCExprGen;
+ var terms = new List<Expr>();
+ foreach (var tup in impl2FuncCalls[impl])
+ {
+ var controlVar = tup.Item2;
+ var exprVars = tup.Item3;
+ var varList = new List<Expr>();
+ exprVars.Args.OfType<Expr>().Iter(v => varList.Add(v));
+
+ var args = new List<Expr>();
+ controlVar.InParams.Iter(v => args.Add(Expr.Ident(v)));
+ Expr term = Expr.Eq(new NAryExpr(Token.NoToken, new FunctionCall(controlVar), args),
+ function2Value[tup.Item1].Gamma(varList));
+
+ if (controlVar.InParams.Count != 0)
+ {
+ term = new ForallExpr(Token.NoToken, new List<Variable>(controlVar.InParams.ToArray()),
+ new Trigger(Token.NoToken, true, new List<Expr> { new NAryExpr(Token.NoToken, new FunctionCall(controlVar), args) }),
+ term);
+ }
+ terms.Add(term);
+ }
+ var env = Expr.BinaryTreeAnd(terms);
+
+ env.Typecheck(new TypecheckingContext((IErrorSink)null));
+ var envVC = prover.Context.BoogieExprTranslator.Translate(env);
+
+ var vc = gen.Implies(envVC, impl2VC[impl]);
+
+ if (CommandLineOptions.Clo.Trace)
+ {
+ Console.WriteLine("Verifying {0}: ", impl);
+ //Console.WriteLine("env: {0}", envVC);
+ var envFuncs = new HashSet<string>();
+ impl2FuncCalls[impl].Iter(tup => envFuncs.Add(tup.Item1));
+ envFuncs.Iter(f => PrintFunction(existentialFunctions[f]));
+ }
+
+ var handler = impl2ErrorHandler[impl].Item1;
+ var collector = impl2ErrorHandler[impl].Item2;
+ collector.Reset(impl);
+
+ var start = DateTime.Now;
+
+ prover.Push();
+ prover.Assert(gen.Not(vc), true);
+ prover.FlushAxiomsToTheoremProver();
+ prover.Check();
+ ProverInterface.Outcome proverOutcome = prover.CheckOutcomeCore(handler);
+
+ //prover.BeginCheck(impl, vc, handler);
+ //ProverInterface.Outcome proverOutcome = prover.CheckOutcomeCore(handler);
+
+ var inc = (DateTime.Now - start);
+ proverTime += inc;
+ numProverQueries++;
+
+ if (CommandLineOptions.Clo.Trace)
+ Console.WriteLine("Time taken = " + inc.TotalSeconds.ToString());
+
+ if (proverOutcome == ProverInterface.Outcome.TimeOut || proverOutcome == ProverInterface.Outcome.OutOfMemory)
+ {
+ // pick some function; make it true and keep going
+ bool changed = false;
+ foreach (var f in impl2functionsAsserted[impl])
+ {
+ function2Value[f] = function2Value[f].MakeTop(out changed);
+ if (changed) break;
+ }
+ if(!changed)
+ return new VCGenOutcome(proverOutcome, new List<Counterexample>());
+ }
+
+ if (CommandLineOptions.Clo.Trace)
+ Console.WriteLine(collector.numErrors > 0 ? "SAT" : "UNSAT");
+
+ if (collector.numErrors > 0)
+ {
+ var funcsChanged = collector.funcsChanged;
+ if (funcsChanged.Count == 0)
+ {
+ overallOutcome = new VCGenOutcome(ProverInterface.Outcome.Invalid, collector.errors);
+ break;
+ }
+
+ // propagate dependent guys back into the worklist, including self
+ var deps = new HashSet<string>();
+ deps.Add(impl);
+ funcsChanged.Iter(f => deps.UnionWith(function2implAssumed[f]));
+
+ deps.Iter(s => worklist.Add(Tuple.Create(impl2Priority[s], s)));
+ }
+
+ prover.Pop();
+ }
+
+ if (CommandLineOptions.Clo.Trace)
+ {
+ Console.WriteLine("Prover time = {0}", proverTime.TotalSeconds.ToString("F2"));
+ Console.WriteLine("Number of prover queries = " + numProverQueries);
+ }
+
+ if (CommandLineOptions.Clo.PrintAssignment)
+ {
+ // Print the answer
+ existentialFunctions.Values.Iter(PrintFunction);
+ }
+
+ return overallOutcome;
+ }
+
+
+ public IEnumerable<Function> GetAssignment()
+ {
+ var ret = new List<Function>();
+ foreach (var func in existentialFunctions.Values)
+ {
+ var invars = new List<Expr>(func.InParams.OfType<Variable>().Select(v => Expr.Ident(v)));
+ func.Body = function2Value[func.Name].Gamma(invars);
+ ret.Add(func);
+ }
+ return ret;
+ }
+
+ private void PrintFunction(Function function)
+ {
+ var tt = new TokenTextWriter(Console.Out, /*pretty=*/ false);
+ var invars = new List<Expr>(function.InParams.OfType<Variable>().Select(v => Expr.Ident(v)));
+ function.Body = function2Value[function.Name].Gamma(invars);
+ function.Emit(tt, 0);
+ tt.Close();
+ }
+
+ public HashSet<string> HandleCounterExample(string impl, Counterexample error)
+ {
+ var funcsChanged = new HashSet<string>();
+ // Find the failing assert -- need to do a join there
+ // return the set of functions whose definition has changed
+ var cex = ExtractState(impl, error);
+ foreach (var tup in cex)
+ {
+ function2Value[tup.Item1] = function2Value[tup.Item1].Join(tup.Item2);
+ funcsChanged.Add(tup.Item1);
+ }
+ return funcsChanged;
+ }
+
+ private List<Tuple<string, List<Model.Element>>> ExtractState(string impl, Counterexample error)
+ {
+ var lastBlock = error.Trace.Last() as Block;
+ AssertCmd failingAssert = null;
+
+ CallCounterexample callCounterexample = error as CallCounterexample;
+ if (callCounterexample != null)
+ {
+ Procedure failingProcedure = callCounterexample.FailingCall.Proc;
+ Requires failingRequires = callCounterexample.FailingRequires;
+ failingAssert = lastBlock.Cmds.OfType<AssertRequiresCmd>().FirstOrDefault(ac => ac.Requires == failingRequires);
+ }
+ ReturnCounterexample returnCounterexample = error as ReturnCounterexample;
+ if (returnCounterexample != null)
+ {
+ Ensures failingEnsures = returnCounterexample.FailingEnsures;
+ failingAssert = lastBlock.Cmds.OfType<AssertEnsuresCmd>().FirstOrDefault(ac => ac.Ensures == failingEnsures);
+ }
+ AssertCounterexample assertCounterexample = error as AssertCounterexample;
+ if (assertCounterexample != null)
+ {
+ failingAssert = lastBlock.Cmds.OfType<AssertCmd>().FirstOrDefault(ac => ac == assertCounterexample.FailingAssert);
+ }
+
+ Debug.Assert(failingAssert != null);
+ return ExtractState(impl, failingAssert.Expr, error.Model);
+ }
+
+ private static int existentialConstCounter = 0;
+
+ private List<Tuple<string, List<Model.Element>>> ExtractState(string impl, Expr expr, Model model)
+ {
+ var funcsUsed = FunctionCollector.Collect(expr);
+
+ var ret = new List<Tuple<string, List<Model.Element>>>();
+
+ foreach (var tup in funcsUsed.Where(t => t.Item2 == null))
+ {
+ var constant = tup.Item1;
+ if (!constant2FuncCall.ContainsKey(constant.Name))
+ continue;
+
+ var func = constant2FuncCall[constant.Name];
+ var funcName = (func.Fun as FunctionCall).FunctionName;
+ var vals = new List<Model.Element>();
+ prover.Context.BoogieExprTranslator.Translate(func.Args).Iter(ve => vals.Add(getValue(ve, model)));
+ ret.Add(Tuple.Create(funcName, vals));
+ }
+
+ foreach (var tup in funcsUsed.Where(t => t.Item2 != null))
+ {
+ var constant = tup.Item1;
+ var boundExpr = tup.Item2;
+
+ if (!constant2FuncCall.ContainsKey(constant.Name))
+ continue;
+
+ // There are some bound variables (because the existential function was inside an \exists).
+ // We must find an assignment for bound varibles
+
+ // First, peice apart the existential functions
+ var cd = new Duplicator();
+ var tup2 = ExistentialExprModelMassage.Massage(cd.VisitExpr(boundExpr.Body));
+ var be = tup2.Item1;
+ Expr env = Expr.True;
+ foreach (var ahFunc in tup2.Item2)
+ {
+ var tup3 = impl2FuncCalls[impl].First(t => t.Item2.Name == ahFunc.Name);
+ var varList = new List<Expr>();
+ tup3.Item3.Args.OfType<Expr>().Iter(v => varList.Add(v));
+
+ env = Expr.And(env, function2Value[tup3.Item1].Gamma(varList));
+ }
+ be = Expr.And(be, Expr.Not(env));
+
+ // map formals to constants
+ var formalToConstant = new Dictionary<string, Constant>();
+ foreach (var f in boundExpr.Dummies.OfType<Variable>())
+ formalToConstant.Add(f.Name, new Constant(Token.NoToken, new TypedIdent(Token.NoToken, f.Name + "@subst@" + (existentialConstCounter++), f.TypedIdent.Type), false));
+ be = Substituter.Apply(new Substitution(v => formalToConstant.ContainsKey(v.Name) ? Expr.Ident(formalToConstant[v.Name]) : Expr.Ident(v)), be);
+ formalToConstant.Values.Iter(v => prover.Context.DeclareConstant(v, false, null));
+
+ var reporter = new AbstractHoudiniErrorReporter();
+ var ve = prover.Context.BoogieExprTranslator.Translate(be);
+ prover.Assert(ve, true);
+ prover.Check();
+ var proverOutcome = prover.CheckOutcomeCore(reporter);
+ if (proverOutcome != ProverInterface.Outcome.Invalid)
+ continue;
+ model = reporter.model;
+
+ var func = constant2FuncCall[constant.Name];
+ var funcName = (func.Fun as FunctionCall).FunctionName;
+ var vals = new List<Model.Element>();
+ foreach (var funcArg in func.Args.OfType<Expr>())
+ {
+ var arg = Substituter.Apply(new Substitution(v => formalToConstant.ContainsKey(v.Name) ? Expr.Ident(formalToConstant[v.Name]) : Expr.Ident(v)), funcArg);
+ vals.Add(getValue(prover.Context.BoogieExprTranslator.Translate(arg), model));
+ }
+ ret.Add(Tuple.Create(funcName, vals));
+
+ }
+
+ return ret;
+ }
+
+ private Model.Element getValue(VCExpr arg, Model model)
+ {
+
+
+ if (arg is VCExprLiteral)
+ {
+ //return model.GetElement(arg.ToString());
+ return model.MkElement(arg.ToString());
+ }
+
+ else if (arg is VCExprVar)
+ {
+ var el = model.TryGetFunc(prover.Context.Lookup(arg as VCExprVar));
+ if (el != null)
+ {
+ Debug.Assert(el.Arity == 0 && el.AppCount == 1);
+ return el.Apps.First().Result;
+ }
+ else
+ {
+ // Variable not defined; assign arbitrary value
+ if (arg.Type.IsBool)
+ return model.MkElement("false");
+ else if (arg.Type.IsInt)
+ return model.MkIntElement(0);
+ else
+ return null;
+ }
+ }
+ else if (arg is VCExprNAry && (arg as VCExprNAry).Op is VCExprBvOp)
+ {
+ // support for BV constants
+ var bvc = (arg as VCExprNAry)[0] as VCExprLiteral;
+ if (bvc != null)
+ {
+ var ret = model.TryMkElement(bvc.ToString() + arg.Type.ToString());
+ if (ret != null && (ret is Model.BitVector)) return ret;
+ }
+ }
+
+ object val;
+
+ try
+ {
+ val = prover.Evaluate(arg);
+ }
+ catch (ProverInterface.VCExprEvaluationException)
+ {
+ Console.WriteLine("AbsHoudni: Error evaluating expression {0}", arg);
+ throw;
+ }
+
+ if (val is int || val is bool || val is Microsoft.Basetypes.BigNum)
+ {
+ return model.MkElement(val.ToString());
+ }
+ else
+ {
+ Debug.Assert(false);
+ }
+ return null;
+ }
+
+ // Remove functions AbsHoudiniConstant from the expressions and substitute them with "true"
+ class ExistentialExprModelMassage : StandardVisitor
+ {
+ List<Function> ahFuncs;
+
+ public ExistentialExprModelMassage()
+ {
+ ahFuncs = new List<Function>();
+ }
+
+ public static Tuple<Expr, List<Function>> Massage(Expr expr)
+ {
+ var ee = new ExistentialExprModelMassage();
+ expr = ee.VisitExpr(expr);
+ return Tuple.Create(expr, ee.ahFuncs);
+ }
+
+ public override Expr VisitNAryExpr(NAryExpr node)
+ {
+ if (node.Fun is FunctionCall && (node.Fun as FunctionCall).FunctionName.StartsWith("AbsHoudiniConstant"))
+ {
+ ahFuncs.Add((node.Fun as FunctionCall).Func);
+ return Expr.True;
+ }
+
+ return base.VisitNAryExpr(node);
+ }
+ }
+
+ class FunctionCollector : ReadOnlyVisitor
+ {
+ public List<Tuple<Function, ExistsExpr>> functionsUsed;
+ ExistsExpr existentialExpr;
+
+ public FunctionCollector()
+ {
+ functionsUsed = new List<Tuple<Function, ExistsExpr>>();
+ existentialExpr = null;
+ }
+
+ public static List<Tuple<Function, ExistsExpr>> Collect(Expr expr)
+ {
+ var fv = new FunctionCollector();
+ fv.VisitExpr(expr);
+ return fv.functionsUsed;
+ }
+
+ public override QuantifierExpr VisitQuantifierExpr(QuantifierExpr node)
+ {
+ var oldE = existentialExpr;
+
+ if (node is ExistsExpr)
+ existentialExpr = (node as ExistsExpr);
+
+ node = base.VisitQuantifierExpr(node);
+
+ existentialExpr = oldE;
+ return node;
+ }
+
+ public override Expr VisitNAryExpr(NAryExpr node)
+ {
+ if (node.Fun is FunctionCall)
+ {
+ var collector = new VariableCollector();
+ collector.Visit(node);
+
+ if(existentialExpr != null && existentialExpr.Dummies.Intersect(collector.usedVars).Any())
+ functionsUsed.Add(Tuple.Create((node.Fun as FunctionCall).Func, existentialExpr));
+ else
+ functionsUsed.Add(Tuple.Create<Function, ExistsExpr>((node.Fun as FunctionCall).Func, null));
+ }
+
+ return base.VisitNAryExpr(node);
+ }
+ }
+
+ class AbsHoudiniCounterexampleCollector : VerifierCallback
+ {
+ public HashSet<string> funcsChanged;
+ public string currImpl;
+ public int numErrors;
+ public List<Counterexample> errors;
+
+ AbsHoudini container;
+
+ public AbsHoudiniCounterexampleCollector(AbsHoudini container)
+ {
+ this.container = container;
+ Reset(null);
+ }
+
+ public void Reset(string impl)
+ {
+ funcsChanged = new HashSet<string>();
+ currImpl = impl;
+ numErrors = 0;
+ errors = new List<Counterexample>();
+ }
+
+ public override void OnCounterexample(Counterexample ce, string reason)
+ {
+ numErrors++;
+ errors.Add(ce);
+
+ funcsChanged.UnionWith(
+ container.HandleCounterExample(currImpl, ce));
+ }
+ }
+
+ private void GenVC(Implementation impl)
+ {
+ ModelViewInfo mvInfo;
+ Dictionary<int, Absy> label2absy;
+ var collector = new AbsHoudiniCounterexampleCollector(this);
+ collector.OnProgress("HdnVCGen", 0, 0, 0.0);
+
+ if (CommandLineOptions.Clo.Trace)
+ {
+ Console.WriteLine("Generating VC of {0}", impl.Name);
+ }
+
+ vcgen.ConvertCFG2DAG(impl);
+ var gotoCmdOrigins = vcgen.PassifyImpl(impl, out mvInfo);
+
+ // Inline functions
+ (new InlineFunctionCalls()).VisitBlockList(impl.Blocks);
+
+
+ StripOutermostForall(impl);
+ //ExtractQuantifiedExprs(impl);
+ ExtractBoolExprs(impl);
+
+ //CommandLineOptions.Clo.PrintInstrumented = true;
+ //using (var tt = new TokenTextWriter(Console.Out))
+ // impl.Emit(tt, 0);
+
+ // Intercept the FunctionCalls of the existential functions, and replace them with Boolean constants
+ var existentialFunctionNames = new HashSet<string>(existentialFunctions.Keys);
+ var fv = new ReplaceFunctionCalls(existentialFunctionNames);
+ fv.VisitBlockList(impl.Blocks);
+
+ //using (var tt = new TokenTextWriter(Console.Out))
+ // impl.Emit(tt, 0);
+
+
+ impl2functionsAsserted.Add(impl.Name, fv.functionsAsserted);
+ impl2functionsAssumed.Add(impl.Name, fv.functionsAssumed);
+
+ fv.functionsAssumed.Iter(f => function2implAssumed[f].Add(impl.Name));
+ fv.functionsAsserted.Iter(f => function2implAsserted[f].Add(impl.Name));
+
+ impl2FuncCalls.Add(impl.Name, fv.functionsUsed);
+ fv.functionsUsed.Iter(tup => constant2FuncCall.Add(tup.Item2.Name, tup.Item3));
+
+ var gen = prover.VCExprGen;
+ VCExpr controlFlowVariableExpr = CommandLineOptions.Clo.UseLabels ? null : gen.Integer(Microsoft.Basetypes.BigNum.ZERO);
+
+ var vcexpr = vcgen.GenerateVC(impl, controlFlowVariableExpr, out label2absy, prover.Context);
+ if (!CommandLineOptions.Clo.UseLabels)
+ {
+ VCExpr controlFlowFunctionAppl = gen.ControlFlowFunctionApplication(gen.Integer(Microsoft.Basetypes.BigNum.ZERO), gen.Integer(Microsoft.Basetypes.BigNum.ZERO));
+ VCExpr eqExpr = gen.Eq(controlFlowFunctionAppl, gen.Integer(Microsoft.Basetypes.BigNum.FromInt(impl.Blocks[0].UniqueId)));
+ vcexpr = gen.Implies(eqExpr, vcexpr);
+ }
+
+ ProverInterface.ErrorHandler handler = null;
+ if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Local)
+ handler = new VCGen.ErrorReporterLocal(gotoCmdOrigins, label2absy, impl.Blocks, vcgen.incarnationOriginMap, collector, mvInfo, prover.Context, program);
+ else
+ handler = new VCGen.ErrorReporter(gotoCmdOrigins, label2absy, impl.Blocks, vcgen.incarnationOriginMap, collector, mvInfo, prover.Context, program);
+
+ impl2ErrorHandler.Add(impl.Name, Tuple.Create(handler, collector));
+
+ //Console.WriteLine("VC of {0}: {1}", impl.Name, vcexpr);
+
+ // Create a macro so that the VC can sit with the theorem prover
+ Macro macro = new Macro(Token.NoToken, impl.Name + "Macro", new List<Variable>(), new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", Bpl.Type.Bool), false));
+ prover.DefineMacro(macro, vcexpr);
+
+ // Store VC
+ impl2VC.Add(impl.Name, gen.Function(macro));
+
+ // HACK: push the definitions of constants involved in function calls
+ // It is possible that some constants only appear in function calls. Thus, when
+ // they are replaced by Boolean constants, it is possible that (get-value) will
+ // fail if the expression involves such constants. All we need to do is make sure
+ // these constants are declared, because otherwise, semantically we are doing
+ // the right thing.
+ foreach (var tup in fv.functionsUsed)
+ {
+ // Ignore ones with bound varibles
+ if (tup.Item2.InParams.Count > 0) continue;
+ var tt = prover.Context.BoogieExprTranslator.Translate(tup.Item3);
+ tt = prover.VCExprGen.Or(VCExpressionGenerator.True, tt);
+ prover.Assert(tt, true);
+ }
+ }
+
+ // convert "foo(... forall e ...) to:
+ // (p iff forall e) ==> foo(... p ...)
+ // where p is a fresh boolean variable and foo is an existential constant
+ private void ExtractQuantifiedExprs(Implementation impl)
+ {
+ var funcs = new HashSet<string>(existentialFunctions.Keys);
+ foreach (var blk in impl.Blocks)
+ {
+ foreach (var acmd in blk.Cmds.OfType<AssertCmd>())
+ {
+ var ret = ExtractQuantifiers.Extract(acmd.Expr, funcs);
+ acmd.Expr = ret.Item1;
+ impl.LocVars.AddRange(ret.Item2);
+ }
+ }
+ }
+
+ // convert "foo(... e ...) to:
+ // (p iff e) ==> foo(... p ...)
+ // where p is a fresh boolean variable, foo is an existential constant
+ // and e is a Boolean-typed argument of foo
+ private void ExtractBoolExprs(Implementation impl)
+ {
+ var funcs = new HashSet<string>(existentialFunctions.Keys);
+ foreach (var blk in impl.Blocks)
+ {
+ foreach (var acmd in blk.Cmds.OfType<AssertCmd>())
+ {
+ var ret = ExtractBoolArgs.Extract(acmd.Expr, funcs);
+ acmd.Expr = ret.Item1;
+ impl.LocVars.AddRange(ret.Item2);
+ }
+ }
+ }
+
+ // convert "assert e1 && forall x: e2" to
+ // assert e1 && e2[x <- x@bound]
+ private void StripOutermostForall(Implementation impl)
+ {
+ var funcs = new HashSet<string>(existentialFunctions.Keys);
+ foreach (var blk in impl.Blocks)
+ {
+ foreach (var acmd in blk.Cmds.OfType<AssertCmd>())
+ {
+ var ret = StripQuantifiers.Run(acmd.Expr, funcs);
+ acmd.Expr = ret.Item1;
+ impl.LocVars.AddRange(ret.Item2);
+ }
+ }
+ }
+
+ private void Inline()
+ {
+ if (CommandLineOptions.Clo.InlineDepth < 0)
+ return;
+
+ var callGraph = BuildCallGraph();
+
+ foreach (Implementation impl in callGraph.Nodes)
+ {
+ InlineEnsuresVisitor inlineEnsuresVisitor = new InlineEnsuresVisitor();
+ inlineEnsuresVisitor.Visit(impl);
+ }
+
+ foreach (Implementation impl in callGraph.Nodes)
+ {
+ impl.OriginalBlocks = impl.Blocks;
+ impl.OriginalLocVars = impl.LocVars;
+ }
+ foreach (Implementation impl in callGraph.Nodes)
+ {
+ CommandLineOptions.Inlining savedOption = CommandLineOptions.Clo.ProcedureInlining;
+ CommandLineOptions.Clo.ProcedureInlining = CommandLineOptions.Inlining.Spec;
+ Inliner.ProcessImplementationForHoudini(program, impl);
+ CommandLineOptions.Clo.ProcedureInlining = savedOption;
+ }
+ foreach (Implementation impl in callGraph.Nodes)
+ {
+ impl.OriginalBlocks = null;
+ impl.OriginalLocVars = null;
+ }
+
+ Graph<Implementation> oldCallGraph = callGraph;
+ callGraph = new Graph<Implementation>();
+ foreach (Implementation impl in oldCallGraph.Nodes)
+ {
+ callGraph.AddSource(impl);
+ }
+ foreach (Tuple<Implementation, Implementation> edge in oldCallGraph.Edges)
+ {
+ callGraph.AddEdge(edge.Item1, edge.Item2);
+ }
+ int count = CommandLineOptions.Clo.InlineDepth;
+ while (count > 0)
+ {
+ foreach (Implementation impl in oldCallGraph.Nodes)
+ {
+ List<Implementation> newNodes = new List<Implementation>();
+ foreach (Implementation succ in callGraph.Successors(impl))
+ {
+ newNodes.AddRange(oldCallGraph.Successors(succ));
+ }
+ foreach (Implementation newNode in newNodes)
+ {
+ callGraph.AddEdge(impl, newNode);
+ }
+ }
+ count--;
+ }
+ }
+
+ private Graph<Implementation> BuildCallGraph()
+ {
+ Graph<Implementation> callGraph = new Graph<Implementation>();
+ Dictionary<Procedure, HashSet<Implementation>> procToImpls = new Dictionary<Procedure, HashSet<Implementation>>();
+ foreach (var proc in program.Procedures)
+ {
+ procToImpls[proc] = new HashSet<Implementation>();
+ }
+ foreach (var impl in program.Implementations)
+ {
+ if (impl.SkipVerification) continue;
+ callGraph.AddSource(impl);
+ procToImpls[impl.Proc].Add(impl);
+ }
+ foreach (var impl in program.Implementations)
+ {
+ if (impl.SkipVerification) continue;
+ foreach (Block b in impl.Blocks)
+ {
+ foreach (Cmd c in b.Cmds)
+ {
+ CallCmd cc = c as CallCmd;
+ if (cc == null) continue;
+ foreach (Implementation callee in procToImpls[cc.Proc])
+ {
+ callGraph.AddEdge(impl, callee);
+ }
+ }
+ }
+ }
+ return callGraph;
+ }
+
+ }
+
+ class InlineFunctionCalls : StandardVisitor
+ {
+ public Stack<string> inlinedFunctionsStack;
+
+ public InlineFunctionCalls()
+ {
+ inlinedFunctionsStack = new Stack<string>();
+ }
+
+ public override Expr VisitNAryExpr(NAryExpr node)
+ {
+ var fc = node.Fun as FunctionCall;
+ if (fc != null && fc.Func.Body != null && QKeyValue.FindBoolAttribute(fc.Func.Attributes, "inline"))
+ {
+ if (inlinedFunctionsStack.Contains(fc.Func.Name))
+ {
+ // recursion detected
+ throw new AbsHoudiniInternalError("Recursion detected in function declarations");
+ }
+
+ // create a substitution
+ var subst = new Dictionary<Variable, Expr>();
+ for (int i = 0; i < node.Args.Count; i++)
+ {
+ subst.Add(fc.Func.InParams[i], node.Args[i]);
+ }
+
+ var e =
+ Substituter.Apply(new Substitution(v => subst.ContainsKey(v) ? subst[v] : Expr.Ident(v)), fc.Func.Body);
+
+ inlinedFunctionsStack.Push(fc.Func.Name);
+
+ e = base.VisitExpr(e);
+
+ inlinedFunctionsStack.Pop();
+
+ return e;
+ }
+ return base.VisitNAryExpr(node);
+ }
+ }
+
+ class ReplaceFunctionCalls : StandardVisitor
+ {
+ public List<Tuple<string, Function, NAryExpr>> functionsUsed;
+ public List<Function> boolConstants;
+
+ public HashSet<string> functionsAssumed;
+ public HashSet<string> functionsAsserted;
+ HashSet<string> functionsToReplace;
+
+ private bool inAssume;
+ private bool inAssert;
+ private bool inFunction;
+ private List<Dictionary<string, Variable>> boundVars;
+ private static int IdCounter = 0;
+
+ public ReplaceFunctionCalls(HashSet<string> functionsToReplace)
+ {
+ this.functionsUsed = new List<Tuple<string, Function, NAryExpr>>();
+ this.functionsToReplace = functionsToReplace;
+ this.functionsAsserted = new HashSet<string>();
+ this.functionsAssumed = new HashSet<string>();
+ this.boolConstants = new List<Function>();
+ this.boundVars = new List<Dictionary<string, Variable>>();
+
+ inAssume = false;
+ inAssert = false;
+ inFunction = false;
+ }
+
+ public override Cmd VisitAssertCmd(AssertCmd node)
+ {
+ inAssert = true;
+ var ret = base.VisitAssertCmd(node);
+ inAssert = false;
+ return ret;
+ }
+
+ public override Cmd VisitAssertEnsuresCmd(AssertEnsuresCmd node)
+ {
+ return this.VisitAssertCmd(node);
+ }
+
+ public override Cmd VisitAssertRequiresCmd(AssertRequiresCmd node)
+ {
+ return this.VisitAssertCmd(node);
+ }
+
+ public override Cmd VisitAssumeCmd(AssumeCmd node)
+ {
+ inAssume = true;
+ var ret = base.VisitAssumeCmd(node);
+ inAssume = false;
+ return ret;
+ }
+
+ public override QuantifierExpr VisitQuantifierExpr(QuantifierExpr node)
+ {
+ // gather the quantified variables
+ var dummies = new Dictionary<string, Variable>();
+ node.Dummies.Iter(v => dummies.Add(v.Name, v));
+
+ boundVars.Add(dummies);
+
+ node = base.VisitQuantifierExpr(node);
+
+ boundVars.RemoveAt(boundVars.Count - 1);
+
+ return node;
+ }
+
+ public override Expr VisitNAryExpr(NAryExpr node)
+ {
+ var inF = inFunction;
+
+ if (node.Fun is FunctionCall && functionsToReplace.Contains((node.Fun as FunctionCall).FunctionName))
+ {
+ found((node.Fun as FunctionCall).FunctionName);
+ inFunction = true;
+
+ // collect all the variables used by this function
+ var collector = new VariableCollector();
+ collector.VisitExpr(node);
+
+ // Find the outermost bound variables
+ var bound = new List<Variable>();
+ if(boundVars.Count > 0)
+ bound.AddRange(collector.usedVars.Intersect(boundVars[0].Values));
+
+ // create boolean function to replace this guy
+ var constant = new Function(Token.NoToken, "AbsHoudiniConstant" + IdCounter, bound,
+ new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "r", Microsoft.Boogie.Type.Bool), false));
+ IdCounter++;
+
+ functionsUsed.Add(Tuple.Create((node.Fun as FunctionCall).FunctionName, constant, node));
+ boolConstants.Add(constant);
+
+ var args = new List<Expr>();
+ bound.OfType<Variable>().Select(v => Expr.Ident(v)).Iter(v => args.Add(v));
+ return new NAryExpr(Token.NoToken, new FunctionCall(constant), args);
+ }
+ var ret = base.VisitNAryExpr(node);
+
+ inFunction = inF;
+
+ return ret;
+ }
+
+ public override Expr VisitIdentifierExpr(IdentifierExpr node)
+ {
+ if (inFunction)
+ {
+ // Inside functions we can only refer to the outermost bound variables
+ for (int i = boundVars.Count - 1; i >= 1; i--)
+ {
+ if (boundVars[i].ContainsKey(node.Name))
+ throw new AbsHoudiniInternalError("Existential functions can only refer to outermost bound variables in an expression");
+ }
+ }
+
+ return base.VisitIdentifierExpr(node);
+ }
+
+ private void found(string func)
+ {
+ if (inAssume) functionsAssumed.Add(func);
+ if (inAssert) functionsAsserted.Add(func);
+ }
+
+ }
+
+ // convert "foo(... e ...) to:
+ // (p iff e) ==> foo(... p ...)
+ // where p is a fresh boolean variable, foo is an existential constant
+ // and e is a Boolean-typed argument of foo
+ class ExtractBoolArgs : StandardVisitor
+ {
+ static int freshConstCounter = 0;
+ HashSet<string> existentialFunctions;
+ HashSet<Constant> newConstants;
+
+ private ExtractBoolArgs(HashSet<string> existentialFunctions)
+ {
+ this.existentialFunctions = existentialFunctions;
+ this.newConstants = new HashSet<Constant>();
+ }
+
+ public static Tuple<Expr, IEnumerable<Constant>> Extract(Expr expr, HashSet<string> existentialFunctions)
+ {
+ var eq = new ExtractBoolArgs(existentialFunctions);
+ expr = eq.VisitExpr(expr);
+ return Tuple.Create(expr, eq.newConstants.AsEnumerable());
+ }
+
+ public override Expr VisitNAryExpr(NAryExpr node)
+ {
+ if (node.Fun is FunctionCall && existentialFunctions.Contains((node.Fun as FunctionCall).FunctionName))
+ {
+ var constants = new Dictionary<Constant, Expr>();
+ for (int i = 0; i < node.Args.Count; i++)
+ {
+ if (node.Args[i].Type == Type.Bool)
+ {
+ var constant = new Constant(Token.NoToken, new TypedIdent(Token.NoToken,
+ "boolArg@const" + freshConstCounter, Microsoft.Boogie.Type.Bool), false);
+ freshConstCounter++;
+ constants.Add(constant, node.Args[i]);
+ node.Args[i] = Expr.Ident(constant);
+ }
+ }
+
+ newConstants.UnionWith(constants.Keys);
+
+ Expr ret = Expr.True;
+ foreach (var tup in constants)
+ {
+ ret = Expr.And(ret, Expr.Eq(Expr.Ident(tup.Key), tup.Value));
+ }
+ return Expr.Imp(ret, node);
+ }
+
+ return base.VisitNAryExpr(node);
+ }
+ }
+
+
+ // convert "foo(... forall e ...) to:
+ // (p iff forall e) ==> foo(... p ...)
+ // where p is a fresh boolean variable and foo is an existential constant
+ class ExtractQuantifiers : StandardVisitor
+ {
+ static int freshConstCounter = 0;
+ HashSet<string> existentialFunctions;
+ bool insideExistential;
+ Dictionary<Constant, Expr> newConstants;
+
+ private ExtractQuantifiers(HashSet<string> existentialFunctions)
+ {
+ this.existentialFunctions = existentialFunctions;
+ insideExistential = false;
+ newConstants = new Dictionary<Constant, Expr>();
+ }
+
+ public static Tuple<Expr, IEnumerable<Constant>> Extract(Expr expr, HashSet<string> existentialFunctions)
+ {
+ var eq = new ExtractQuantifiers(existentialFunctions);
+ expr = eq.VisitExpr(expr);
+ Expr ret = Expr.True;
+ foreach (var tup in eq.newConstants)
+ {
+ ret = Expr.And(ret, Expr.Eq(Expr.Ident(tup.Key), tup.Value));
+ }
+ ret = Expr.Imp(ret, expr);
+ return Tuple.Create(ret, eq.newConstants.Keys.AsEnumerable());
+ }
+
+ public override Expr VisitNAryExpr(NAryExpr node)
+ {
+ var oldIE = insideExistential;
+
+ if (node.Fun is FunctionCall && existentialFunctions.Contains((node.Fun as FunctionCall).FunctionName))
+ insideExistential = true;
+
+ var ret = base.VisitNAryExpr(node);
+
+ insideExistential = oldIE;
+ return ret;
+ }
+
+ public override Expr VisitExpr(Expr node)
+ {
+ if (node is QuantifierExpr)
+ {
+ return MyVisitQuantifierExpr(node as QuantifierExpr);
+ }
+ return base.VisitExpr(node);
+ }
+
+ public Expr MyVisitQuantifierExpr(QuantifierExpr node)
+ {
+ node = base.VisitQuantifierExpr(node);
+
+ if (insideExistential)
+ {
+ var constant = new Constant(Token.NoToken, new TypedIdent(Token.NoToken,
+ "quant@const" + freshConstCounter, Microsoft.Boogie.Type.Bool), false);
+ freshConstCounter++;
+
+ newConstants.Add(constant, node);
+
+ return Expr.Ident(constant);
+ }
+
+ return node;
+ }
+ }
+
+ // convert "assert e1 && forall x: e2" to
+ // assert e1 && e2[x <- x@bound]
+ // only if e2 has an existential function
+ class StripQuantifiers : StandardVisitor
+ {
+ static int boundVarCounter = 0;
+
+ // 0 -> None, 1 -> Forall, 2 -> Exists, 3 -> Nested
+ int insideQuantifier;
+
+ bool searchExistentialFunction;
+ bool foundExistentialFunction;
+
+ HashSet<string> existentialFunctions;
+ Dictionary<string, LocalVariable> subst;
+ List<LocalVariable> LocalsToAdd;
+
+ private StripQuantifiers(HashSet<string> existentialFunctions)
+ {
+ this.existentialFunctions = existentialFunctions;
+ insideQuantifier = 0;
+ searchExistentialFunction = false;
+ foundExistentialFunction = false;
+ LocalsToAdd = new List<LocalVariable>();
+ subst = null;
+ }
+
+ public static Tuple<Expr,List<LocalVariable>> Run(Expr expr, HashSet<string> existentialFunctions)
+ {
+ // check for type errors first
+ var sq = new StripQuantifiers(existentialFunctions);
+ var ret = sq.VisitExpr(expr);
+
+ return Tuple.Create(ret, sq.LocalsToAdd);
+ }
+
+ public override Expr VisitExpr(Expr node)
+ {
+ if (node is QuantifierExpr)
+ {
+ return MyVisitQuantifierExpr(node as QuantifierExpr);
+ }
+
+ return base.VisitExpr(node);
+ }
+
+ private Expr MyVisitQuantifierExpr(QuantifierExpr node)
+ {
+ var oldIQ = insideQuantifier;
+ Expr ret = node;
+
+ // update "insideQuantifier"
+ if (insideQuantifier == 0)
+ {
+ if (node is ForallExpr) insideQuantifier = 1;
+ else insideQuantifier = 2;
+ }
+ else if (insideQuantifier > 0)
+ {
+ insideQuantifier = 3;
+ }
+
+ // Going inside Forall?
+ if (insideQuantifier == 1)
+ {
+ // see if there is any existential function inside
+ searchExistentialFunction = true;
+ foundExistentialFunction = false;
+ base.VisitQuantifierExpr(node);
+
+ if (foundExistentialFunction)
+ {
+ // create substitution to apply
+ subst = new Dictionary<string, LocalVariable>();
+ foreach (var bv in node.Dummies.OfType<Variable>())
+ {
+ var lv = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken,
+ bv + "@bound" + boundVarCounter, bv.TypedIdent.Type));
+ boundVarCounter++;
+ subst.Add(bv.Name, lv);
+ LocalsToAdd.Add(lv);
+ }
+
+ // apply the subst
+ var body = base.VisitExpr(node.Body);
+ ret = body;
+
+ subst = null;
+ }
+ else
+ {
+ ret = base.VisitQuantifierExpr(node);
+ }
+
+ searchExistentialFunction = false;
+ foundExistentialFunction = false;
+ }
+ else
+ {
+ ret = base.VisitQuantifierExpr(node);
+ }
+
+ insideQuantifier = oldIQ;
+ return ret;
+ }
+
+ public override Expr VisitNAryExpr(NAryExpr node)
+ {
+ if (node.Fun is FunctionCall && existentialFunctions.Contains((node.Fun as FunctionCall).FunctionName))
+ {
+ if (insideQuantifier == 3)
+ throw new AbsHoudiniInternalError("Existential function found inside exists, or nested foralls");
+
+ if (searchExistentialFunction)
+ foundExistentialFunction = true;
+
+ }
+
+ return base.VisitNAryExpr(node);
+ }
+
+ public override Expr VisitIdentifierExpr(IdentifierExpr node)
+ {
+ if (subst != null && subst.ContainsKey(node.Name))
+ return Expr.Ident(subst[node.Name]);
+ return base.VisitIdentifierExpr(node);
+ }
+
+ }
+
+ public class Intervals : IAbstractDomain
+ {
+ // [lower, upper]
+ int upper;
+ int lower;
+ // or: \bot
+ bool isBottom;
+ // number of times join has been performed
+ int nJoin;
+ // number of times before we widen
+ readonly static int maxJoin = 5;
+
+ public Intervals()
+ {
+ this.upper = 0;
+ this.lower = 0;
+ this.nJoin = 0;
+ this.isBottom = true;
+ }
+
+ private Intervals(int lower, int upper, int nJoin)
+ {
+ this.upper = upper;
+ this.lower = lower;
+ this.nJoin = nJoin;
+ }
+
+ public IAbstractDomain Bottom()
+ {
+ return new Intervals();
+ }
+
+ public IAbstractDomain MakeTop(out bool changed)
+ {
+ if (lower == Int32.MinValue && upper == Int32.MaxValue)
+ {
+ changed = false;
+ return this;
+ }
+ changed = true;
+ return new Intervals(Int32.MinValue, Int32.MaxValue, 0);
+ }
+
+ public IAbstractDomain Join(List<Model.Element> states)
+ {
+ Debug.Assert(states.Count == 1);
+ var state = states[0] as Model.Integer;
+ if (state == null)
+ throw new AbsHoudiniInternalError("Incorrect type, expected int");
+ var intval = state.AsInt();
+
+ if (isBottom)
+ {
+ return new Intervals(intval, intval, 1);
+ }
+
+ if(intval >= lower && intval <= upper)
+ return this;
+
+ if (nJoin > maxJoin)
+ {
+ // widen
+ if (intval > upper)
+ return new Intervals(lower, Int32.MaxValue, 1);
+ if(intval < lower)
+ return new Intervals(Int32.MinValue, upper, 1);
+
+ Debug.Assert(false);
+ }
+
+ if (intval > upper)
+ return new Intervals(lower, intval, nJoin + 1);
+ if (intval < lower)
+ return new Intervals(intval, upper, nJoin + 1);
+
+ Debug.Assert(false);
+ return null;
+ }
+
+ public Expr Gamma(List<Expr> vars)
+ {
+ Debug.Assert(vars.Count == 1);
+ var v = vars[0];
+ if (isBottom) return Expr.False;
+ Expr ret = Expr.True;
+ if (lower != Int32.MinValue)
+ ret = Expr.And(ret, Expr.Ge(v, Expr.Literal(lower)));
+ if (upper != Int32.MaxValue)
+ ret = Expr.And(ret, Expr.Le(v, Expr.Literal(upper)));
+ return ret;
+ }
+
+ public bool TypeCheck(List<Type> argTypes, out string msg)
+ {
+ msg = "";
+ if (argTypes.Count != 1)
+ {
+ msg = "Illegal number of arguments";
+ return false;
+ }
+ if (!argTypes[0].IsInt)
+ {
+ msg = "Illegal type, expecting int";
+ return false;
+ }
+ return true;
+ }
+ }
+
+ public class PredicateAbsFullElem : PredicateAbsElem
+ {
+ public PredicateAbsFullElem()
+ : base(1000)
+ { }
+ }
+
+ public class PredicateAbsElem : IAbstractDomain
+ {
+ public static class ExprExt
+ {
+ public static Expr AndSimp(Expr e1, Expr e2)
+ {
+ if (e1 == Expr.True) return e2;
+ if (e2 == Expr.True) return e1;
+ if (e1 == Expr.False || e2 == Expr.False) return Expr.False;
+ return Expr.And(e1, e2);
+ }
+
+ public static Expr OrSimp(Expr e1, Expr e2)
+ {
+ if (e1 == Expr.False) return e2;
+ if (e2 == Expr.False) return e1;
+ if (e1 == Expr.True || e2 == Expr.True) return Expr.True;
+ return Expr.Or(e1, e2);
+ }
+ }
+
+ class Disjunct
+ {
+ HashSet<int> pos;
+ HashSet<int> neg;
+ bool isTrue;
+
+ public Disjunct()
+ {
+ isTrue = true;
+ pos = new HashSet<int>();
+ neg = new HashSet<int>();
+ }
+
+ public Disjunct(IEnumerable<int> pos, IEnumerable<int> neg, int bound)
+ {
+ this.isTrue = false;
+ this.pos = new HashSet<int>(pos);
+ this.neg = new HashSet<int>(neg);
+ if (this.pos.Overlaps(this.neg))
+ {
+ this.isTrue = true;
+ this.pos = new HashSet<int>();
+ this.neg = new HashSet<int>();
+ }
+ if (this.pos.Count + this.neg.Count > bound)
+ {
+ // Set to true
+ this.isTrue = true;
+ this.pos = new HashSet<int>();
+ this.neg = new HashSet<int>();
+ }
+
+ }
+
+ public Disjunct OR(Disjunct that, int bound)
+ {
+ if (isTrue)
+ return this;
+ if (that.isTrue)
+ return that;
+
+ return new Disjunct(this.pos.Concat(that.pos), this.neg.Concat(that.neg), bound);
+ }
+
+ public bool Implies(Disjunct that)
+ {
+ if (isTrue) return that.isTrue;
+ if (that.isTrue) return true;
+
+ return pos.IsSubsetOf(that.pos) && neg.IsSubsetOf(that.neg);
+ }
+
+ public Expr Gamma(List<Expr> vars)
+ {
+ if (isTrue) return Expr.True;
+ Expr ret = Expr.False;
+ pos.Iter(i => ret = ExprExt.OrSimp(ret, vars[i]));
+ neg.Iter(i => ret = ExprExt.OrSimp(ret, Expr.Not(vars[i])));
+ return ret;
+ }
+ }
+
+ // Conjunction of Disjuncts
+ List<Disjunct> conjuncts;
+ int DisjunctBound;
+ bool isFalse;
+
+ public PredicateAbsElem()
+ {
+ this.conjuncts = new List<Disjunct>();
+ this.isFalse = true;
+ this.DisjunctBound = 3;
+ }
+
+ public PredicateAbsElem(int bound)
+ {
+ this.conjuncts = new List<Disjunct>();
+ this.isFalse = true;
+ this.DisjunctBound = bound;
+ }
+
+ public IAbstractDomain Bottom()
+ {
+ return new PredicateAbsElem(DisjunctBound);
+ }
+
+ public IAbstractDomain MakeTop(out bool changed)
+ {
+ if (conjuncts.Count == 0)
+ {
+ changed = false;
+ return this;
+ }
+ changed = true;
+ var ret = new PredicateAbsElem(DisjunctBound);
+ ret.isFalse = false;
+ return ret;
+ }
+
+ public IAbstractDomain Join(List<Model.Element> state)
+ {
+ if (state.Any(me => !(me is Model.Boolean)))
+ throw new AbsHoudiniInternalError("Predicate Abstraction requires that each argument be of type bool");
+
+ // quick return if this == true
+ if (!this.isFalse && conjuncts.Count == 0)
+ return this;
+
+ var ret = new PredicateAbsElem(DisjunctBound);
+ ret.isFalse = false;
+
+ for (int i = 0; i < state.Count; i++)
+ {
+ var b = (state[i] as Model.Boolean).Value;
+ Disjunct d = null;
+ if (b) d = new Disjunct(new int[] { i }, new int[] { }, DisjunctBound);
+ else d = new Disjunct(new int[] { }, new int[] { i }, DisjunctBound);
+
+ if (isFalse)
+ ret.AddDisjunct(d);
+ else
+ {
+ conjuncts.Iter(c => ret.AddDisjunct(c.OR(d, DisjunctBound)));
+ }
+ }
+
+ return ret;
+
+ }
+
+ public Expr Gamma(List<Expr> vars)
+ {
+ if (isFalse) return Expr.False;
+ Expr ret = Expr.True;
+
+ foreach (var c in conjuncts)
+ {
+ ret = ExprExt.AndSimp(ret, c.Gamma(vars));
+ }
+
+ return ret;
+ }
+
+ public bool TypeCheck(List<Type> argTypes, out string msg)
+ {
+ msg = "";
+ if (argTypes.Any(t => !t.IsBool))
+ {
+ msg = "Illegal type, expecting bool";
+ return false;
+ }
+ return true;
+ }
+
+ private void AddDisjunct(Disjunct d)
+ {
+ if (conjuncts.Any(c => c.Implies(d)))
+ return;
+
+ conjuncts.RemoveAll(c => d.Implies(c));
+ conjuncts.Add(d);
+ }
+ }
+
+ // [false -- (x == true) -- true]
+ public class HoudiniConst : IAbstractDomain
+ {
+ bool isBottom;
+ bool isTop;
+
+ private HoudiniConst(bool isTop, bool isBottom)
+ {
+ this.isBottom = isBottom;
+ this.isTop = isTop;
+ Debug.Assert(!(isTop && isBottom));
+ }
+
+ public static HoudiniConst GetExtObj()
+ {
+ return new HoudiniConst(false, false);
+ }
+
+ public static HoudiniConst GetTop()
+ {
+ return new HoudiniConst(true, false);
+ }
+
+ public static HoudiniConst GetBottom()
+ {
+ return new HoudiniConst(false, true);
+ }
+
+ public IAbstractDomain Bottom()
+ {
+ return GetBottom();
+ }
+
+ public IAbstractDomain MakeTop(out bool changed)
+ {
+ changed = false;
+ if (isTop) return this;
+ changed = true;
+ return GetTop();
+ }
+
+ public IAbstractDomain Join(List<Model.Element> states)
+ {
+ Debug.Assert(states.Count == 1);
+ var state = states[0];
+
+ if (isTop) return this;
+
+ if (state is Model.Boolean)
+ {
+ if ((state as Model.Boolean).Value)
+ return GetExtObj();
+ }
+
+ return GetTop();
+ }
+
+ public Expr Gamma(List<Expr> vars)
+ {
+ Debug.Assert(vars.Count == 1);
+ var v = vars[0];
+ if (isBottom) return Expr.False;
+ if (isTop) return Expr.True;
+ return v;
+ }
+
+ public bool TypeCheck(List<Type> argTypes, out string msg)
+ {
+ msg = "";
+ if (argTypes.Count != 1)
+ {
+ msg = "Illegal number of arguments, expecting 1";
+ return false;
+ }
+ if (!argTypes[0].IsBool)
+ {
+ msg = "Illegal type, expecting bool";
+ return false;
+ }
+ return true;
+ }
+ }
+
+ // foo(x) = x < 2^j for some j <= 16
+ public class PowDomain : IAbstractDomain
+ {
+ enum Val { FALSE, NEITHER, TRUE };
+ Val tlevel;
+ bool isBottom { get { return tlevel == Val.FALSE; } }
+ bool isTop { get { return tlevel == Val.TRUE; } }
+
+ readonly int Max = 16;
+
+ int upper; // <= Max
+
+ private PowDomain(Val tlevel) :
+ this(tlevel, 0) { }
+
+ private PowDomain(Val tlevel, int upper)
+ {
+ this.tlevel = tlevel;
+ this.upper = upper;
+ }
+
+ public static IAbstractDomain GetBottom()
+ {
+ return new PowDomain(Val.FALSE) as IAbstractDomain;
+ }
+
+ public IAbstractDomain MakeTop(out bool changed)
+ {
+ if (isTop)
+ {
+ changed = false;
+ return this;
+ }
+ changed = true;
+ return new PowDomain(Val.TRUE);
+ }
+
+ IAbstractDomain IAbstractDomain.Bottom()
+ {
+ return GetBottom();
+ }
+
+ IAbstractDomain IAbstractDomain.Join(List<Model.Element> state)
+ {
+ if (isTop) return this;
+
+ int v = 0;
+ if (state[0] is Model.BitVector)
+ v = (state[0] as Model.BitVector).AsInt();
+ else if (state[0] is Model.Integer)
+ v = (state[0] as Model.Integer).AsInt();
+ else Debug.Assert(false);
+
+ var nupper = upper;
+ while ((1 << nupper) < v) nupper++;
+ var ntlevel = Val.NEITHER;
+ if (nupper > Max) ntlevel = Val.TRUE;
+ return new PowDomain(ntlevel, nupper);
+ }
+
+ Expr IAbstractDomain.Gamma(List<Expr> vars)
+ {
+ if (isBottom) return Expr.False;
+ if (isTop) return Expr.True;
+ var v = vars[0];
+ if (v.Type.IsBv)
+ {
+ var bits = v.Type.BvBits;
+ if (!AbstractDomainFactory.bvslt.ContainsKey(bits))
+ throw new AbsHoudiniInternalError("No builtin function found for bv" + bits.ToString());
+ var bvslt = AbstractDomainFactory.bvslt[bits];
+ return new NAryExpr(Token.NoToken, new FunctionCall(bvslt), new List<Expr> { v,
+ new LiteralExpr(Token.NoToken, Basetypes.BigNum.FromInt(1 << (upper+1)), 32) });
+ }
+ else
+ {
+ return Expr.Lt(v, Expr.Literal(1 << (upper+1)));
+ }
+ }
+
+ bool IAbstractDomain.TypeCheck(List<Type> argTypes, out string msg)
+ {
+ msg = "";
+ if (argTypes.Count != 1)
+ {
+ msg = "Illegal number of arguments, expecting 1";
+ return false;
+ }
+ if (argTypes.Any(tt => !tt.IsInt && !tt.IsBv))
+ {
+ msg = "Illegal type, expecting int or bv";
+ return false;
+ }
+ return true;
+ }
+ }
+
+ // foo(x_i) = all equalities that hold
+ public class EqualitiesDomain : IAbstractDomain
+ {
+ bool isBottom;
+ List<HashSet<int>> equalities;
+
+ public EqualitiesDomain(bool isBottom, List<HashSet<int>> eq)
+ {
+ this.isBottom = isBottom;
+ this.equalities = eq;
+ }
+
+ public static IAbstractDomain GetBottom()
+ {
+ return new EqualitiesDomain(true, new List<HashSet<int>>());
+ }
+
+ IAbstractDomain IAbstractDomain.Bottom()
+ {
+ return GetBottom();
+ }
+
+ public IAbstractDomain MakeTop(out bool changed)
+ {
+ if (equalities.Count == 0)
+ {
+ changed = false;
+ return this;
+ }
+ changed = true;
+ return new EqualitiesDomain(false, new List<HashSet<int>>());
+ }
+
+ IAbstractDomain IAbstractDomain.Join(List<Model.Element> state)
+ {
+ // find the guys that are equal
+ var eq = new List<HashSet<int>>();
+ for (int i = 0; i < state.Count; i++)
+ {
+ var added = false;
+ foreach (var s in eq)
+ {
+ var sv = s.First();
+ if (state[i].ToString() == state[sv].ToString())
+ {
+ s.Add(i);
+ added = true;
+ break;
+ }
+ }
+ if (!added) eq.Add(new HashSet<int>(new int[] { i }));
+ }
+
+ if (isBottom)
+ {
+ return new EqualitiesDomain(false, eq);
+ }
+
+ // intersect two partitions equalities and eq
+ var m1 = GetMap(equalities, state.Count);
+ var m2 = GetMap(eq, state.Count);
+
+ for (int i = 0; i < state.Count; i++)
+ m2[i] = new HashSet<int>(m2[i].Intersect(m1[i]));
+
+
+ // map from representative to set
+ var repToSet = new Dictionary<int, HashSet<int>>();
+
+ for (int i = 0; i < state.Count; i++)
+ {
+ var rep = m2[i].Min();
+ if (!repToSet.ContainsKey(rep))
+ repToSet[rep] = m2[i];
+ }
+
+ var ret = new List<HashSet<int>>();
+ repToSet.Values.Iter(s => ret.Add(s));
+
+ return new EqualitiesDomain(false, ret);
+ }
+
+ Expr IAbstractDomain.Gamma(List<Expr> vars)
+ {
+ if (isBottom) return Expr.False;
+ Expr ret = Expr.True;
+ foreach (var eq in equalities.Select(hs => hs.ToList()))
+ {
+ if (eq.Count == 1) continue;
+ var prev = eq[0];
+ for (int i = 1; i < eq.Count; i++)
+ {
+ ret = Expr.And(ret, Expr.Eq(vars[prev], vars[eq[i]]));
+ prev = eq[i];
+ }
+ }
+ return ret;
+ }
+
+ bool IAbstractDomain.TypeCheck(List<Type> argTypes, out string msg)
+ {
+ msg = "";
+ if (argTypes.Count == 0) return true;
+ var ot = argTypes[0];
+
+ if (argTypes.Any(tt => !tt.Equals(ot)))
+ {
+ msg = string.Format("Illegal type, expecting type {0}, got {1}", ot, argTypes.First(tt => !tt.Equals(ot)));
+ return false;
+ }
+ return true;
+ }
+
+ private HashSet<int>[] GetMap(List<HashSet<int>> eq, int n)
+ {
+ var ret = new HashSet<int>[n];
+ foreach (var s in eq)
+ {
+ foreach (var i in s)
+ ret[i] = s;
+ }
+ return ret;
+ }
+ }
+
+ // foo(a,b) \in {false, \not a, a ==> b, true}
+ public class ImplicationDomain : IAbstractDomain
+ {
+ enum Val {FALSE, NOT_A, A_IMP_B, TRUE};
+ Val val;
+
+ private ImplicationDomain(Val val)
+ {
+ this.val = val;
+ }
+
+ public static ImplicationDomain GetBottom()
+ {
+ return new ImplicationDomain(Val.FALSE);
+ }
+
+ public IAbstractDomain Bottom()
+ {
+ return GetBottom();
+ }
+
+ public IAbstractDomain MakeTop(out bool changed)
+ {
+ if(val == Val.TRUE) {
+ changed = false;
+ return this;
+ }
+ changed = true;
+ return new ImplicationDomain(Val.TRUE);
+ }
+
+ public IAbstractDomain Join(List<Model.Element> states)
+ {
+ Debug.Assert(states.Count == 2);
+ var v1 = (states[0] as Model.Boolean).Value;
+ var v2 = (states[1] as Model.Boolean).Value;
+
+ if (val == Val.TRUE) return this;
+
+ var that = Val.TRUE;
+ if (!v1) that = Val.NOT_A;
+ else if (!v1 || v2) that = Val.A_IMP_B;
+
+ if (that == Val.TRUE || val == Val.FALSE)
+ return new ImplicationDomain(that);
+
+ // Now, neither this or that is FALSE or TRUE
+ if (val == that)
+ return this;
+
+ Debug.Assert(val == Val.A_IMP_B || that == Val.A_IMP_B);
+ return new ImplicationDomain(Val.A_IMP_B);
+ }
+
+ public Expr Gamma(List<Expr> vars)
+ {
+ Debug.Assert(vars.Count == 2);
+
+ var v1 = vars[0];
+ var v2 = vars[1];
+
+ if (val == Val.FALSE) return Expr.False;
+ if (val == Val.TRUE) return Expr.True;
+ if (val == Val.NOT_A) return Expr.Not(v1);
+ return Expr.Imp(v1, v2);
+ }
+
+ public bool TypeCheck(List<Type> argTypes, out string msg)
+ {
+ msg = "";
+ if (argTypes.Count != 2)
+ {
+ msg = "Illegal number of arguments, expecting 2";
+ return false;
+ }
+ if (argTypes.Any(tt => !tt.IsBool))
+ {
+ msg = "Illegal type, expecting bool";
+ return false;
+ }
+ return true;
+ }
+ }
+
+ public class ConstantProp : IAbstractDomain
+ {
+ object val;
+ bool isBottom;
+ bool isTop;
+
+ private ConstantProp(object val, bool isTop, bool isBottom)
+ {
+ this.val = val;
+ this.isBottom = isBottom;
+ this.isTop = isTop;
+ Debug.Assert(!(isTop && isBottom));
+ Debug.Assert(val == null || (val is int) || (val is bool));
+ }
+
+ public static ConstantProp GetExtObj(object val)
+ {
+ Debug.Assert(val != null);
+ return new ConstantProp(val, false, false);
+ }
+
+ public static ConstantProp GetTop()
+ {
+ return new ConstantProp(null, true, false);
+ }
+
+ public static ConstantProp GetBottom()
+ {
+ return new ConstantProp(null, false, true);
+ }
+
+ public IAbstractDomain MakeTop(out bool changed) {
+ if (isTop)
+ {
+ changed = false;
+ return this;
+ }
+ changed = true;
+ return GetTop();
+ }
+
+ private ConstantProp Join(ConstantProp that)
+ {
+ if (isBottom) return that;
+ if (isTop) return this;
+ if (that.isBottom) return this;
+ if (that.isTop) return that;
+
+ if ((val is int) && !(that.val is int))
+ throw new AbsHoudiniInternalError("Type mismatch in ExtObj");
+
+ if ((val is bool) && !(that.val is bool))
+ throw new AbsHoudiniInternalError("Type mismatch in ExtObj");
+
+ if (val is int)
+ {
+ var v1 = (int)val;
+ var v2 = (int)that.val;
+ if (v1 != v2) return GetTop();
+ return this;
+ }
+ else if (val is bool)
+ {
+ var v1 = (bool)val;
+ var v2 = (bool)that.val;
+ if (v1 != v2) return GetTop();
+ return this;
+ }
+ throw new AbsHoudiniInternalError("Illegal val type in ExtObj");
+ }
+
+ public IAbstractDomain Bottom()
+ {
+ return GetBottom();
+ }
+
+ public IAbstractDomain Join(List<Model.Element> states)
+ {
+ Debug.Assert(states.Count == 1);
+ var state = states[0];
+ ConstantProp that = null;
+
+ if (state is Model.Integer)
+ {
+ that = GetExtObj((state as Model.Integer).AsInt());
+ }
+ else if (state is Model.Boolean)
+ {
+ that = GetExtObj((state as Model.Boolean).Value);
+ }
+ else
+ {
+ throw new AbsHoudiniInternalError("Illegal type " + state.GetType().ToString());
+ }
+
+ return Join(that);
+ }
+
+ public Expr Gamma(List<Expr> vars)
+ {
+ Debug.Assert(vars.Count == 1);
+ var v = vars[0];
+ if (isBottom) return Expr.False;
+ if (isTop) return Expr.True;
+ if (val is int)
+ return Expr.Eq(v, Expr.Literal((int)val));
+ if (val is bool && (bool)val)
+ return v;
+ if (val is bool && !(bool)val)
+ return Expr.Not(v);
+
+ return null;
+ }
+
+ public bool TypeCheck(List<Type> argTypes, out string msg)
+ {
+ msg = "";
+ if (argTypes.Count != 1)
+ {
+ msg = "Illegal number of arguments, expecting 1";
+ return false;
+ }
+ if (!argTypes[0].IsInt && ! argTypes[0].IsBool)
+ {
+ msg = "Illegal type, expecting int or bool";
+ return false;
+ }
+ return true;
+ }
+ }
+
+
+ public class IndependentAttribute<T> : IAbstractDomain where T : class, IAbstractDomain
+ {
+ bool isBottom;
+ int numVars;
+ List<T> varVal;
+ T underlyingInstance;
+
+ public IndependentAttribute()
+ {
+ isBottom = true;
+ numVars = 0;
+ varVal = new List<T>();
+ underlyingInstance = null;
+ }
+
+ public IAbstractDomain Bottom()
+ {
+ return new IndependentAttribute<T>();
+ }
+
+ public IAbstractDomain MakeTop(out bool changed)
+ {
+ var mt = new Func<IAbstractDomain>(() =>
+ {
+ var ret = new IndependentAttribute<T>();
+ ret.isBottom = true;
+ ret.numVars = numVars;
+ ret.underlyingInstance = underlyingInstance;
+ ret.varVal = new List<T>();
+ bool tmp;
+ for (int i = 0; i < varVal.Count; i++)
+ ret.varVal.Add(varVal[i].MakeTop(out tmp) as T);
+ return ret;
+ });
+
+ if (!isBottom)
+ {
+ foreach (var t in varVal)
+ {
+ var top = t.MakeTop(out changed);
+ if (changed)
+ {
+ return mt();
+ }
+ }
+ }
+ else
+ {
+ changed = true;
+ return mt();
+ }
+
+ changed = false;
+ return this;
+ }
+ public IAbstractDomain Join(List<Model.Element> state)
+ {
+ SetUnderlyingInstance();
+
+ if (!isBottom && numVars != state.Count)
+ {
+ throw new AbsHoudiniInternalError(
+ string.Format("Got illegal number of arguments ({0}), expected {1}", state.Count, numVars));
+ }
+
+ var ret = new IndependentAttribute<T>();
+ ret.isBottom = false;
+ ret.numVars = state.Count;
+ for(int i = 0; i < state.Count; i++)
+ {
+ var sl = new List<Model.Element>();
+ sl.Add(state[i]);
+ T prev = isBottom ? underlyingInstance.Bottom() as T : varVal[i];
+ ret.varVal.Add(prev.Join(sl) as T);
+ }
+
+ return ret;
+ }
+
+ public Expr Gamma(List<Expr> vars)
+ {
+ if (isBottom) return Expr.False;
+ if (numVars != vars.Count)
+ throw new AbsHoudiniInternalError(
+ string.Format("Got illegal number of arguments ({0}), expected {1}", vars.Count, numVars));
+
+ Expr ret = Expr.True;
+ for (int i = 0; i < numVars; i++)
+ {
+ var sl = new List<Expr>(); sl.Add(vars[i]);
+ ret = Expr.And(ret, varVal[i].Gamma(sl));
+ }
+
+ return ret;
+ }
+
+ private void SetUnderlyingInstance()
+ {
+ if (underlyingInstance != null) return;
+ var tt = typeof(T);
+ underlyingInstance = AbstractDomainFactory.GetInstance(tt) as T;
+ }
+
+ public bool TypeCheck(List<Type> argTypes, out string msg)
+ {
+ SetUnderlyingInstance();
+
+ msg = "";
+ foreach(var t in argTypes)
+ {
+ if(!underlyingInstance.TypeCheck(new List<Type>(new Type[] { t }), out msg))
+ return false;
+ }
+ return true;
+ }
+ }
+
+ public class AbstractDomainFactory
+ {
+ // Type name -> Instance
+ private static Dictionary<string, IAbstractDomain> abstractDomainInstances = new Dictionary<string, IAbstractDomain>();
+ private static Dictionary<string, IAbstractDomain> abstractDomainInstancesFriendly = new Dictionary<string, IAbstractDomain>();
+
+ // bitvector operations
+ public static Dictionary<int, Function> bvslt = new Dictionary<int, Function>();
+
+ public static void Register(string friendlyName, IAbstractDomain instance)
+ {
+ var Name = instance.GetType().FullName;
+ Debug.Assert(!abstractDomainInstances.ContainsKey(Name));
+ abstractDomainInstances.Add(Name, instance);
+ abstractDomainInstancesFriendly.Add(friendlyName, instance);
+ }
+
+ public static IAbstractDomain GetInstance(System.Type type)
+ {
+ var Name = type.FullName;
+ Debug.Assert(abstractDomainInstances.ContainsKey(Name));
+ return abstractDomainInstances[Name] as IAbstractDomain;
+ }
+
+ public static IAbstractDomain GetInstance(string friendlyName)
+ {
+ if (!abstractDomainInstancesFriendly.ContainsKey(friendlyName))
+ {
+ Console.WriteLine("Domain {0} not found", friendlyName);
+ Console.WriteLine("Supported domains are:");
+ abstractDomainInstancesFriendly.Keys.Iter(tup => Console.WriteLine(" {0}", tup));
+ throw new AbsHoudiniInternalError("Domain not found");
+ }
+ return abstractDomainInstancesFriendly[friendlyName] as IAbstractDomain;
+ }
+
+ public static void Initialize(Program program)
+ {
+ // Declare abstract domains
+ var domains = new List<System.Tuple<string, IAbstractDomain>>(new System.Tuple<string, IAbstractDomain>[] {
+ System.Tuple.Create("HoudiniConst", HoudiniConst.GetBottom() as IAbstractDomain),
+ System.Tuple.Create("Intervals", new Intervals() as IAbstractDomain),
+ System.Tuple.Create("ConstantProp", ConstantProp.GetBottom() as IAbstractDomain),
+ System.Tuple.Create("PredicateAbs", new PredicateAbsElem() as IAbstractDomain),
+ System.Tuple.Create("PredicateAbsFull", new PredicateAbsFullElem() as IAbstractDomain),
+ System.Tuple.Create("ImplicationDomain", ImplicationDomain.GetBottom() as IAbstractDomain),
+ System.Tuple.Create("PowDomain", PowDomain.GetBottom() as IAbstractDomain),
+ System.Tuple.Create("EqualitiesDomain", EqualitiesDomain.GetBottom() as IAbstractDomain),
+ System.Tuple.Create("IA[HoudiniConst]", new IndependentAttribute<HoudiniConst>() as IAbstractDomain),
+ System.Tuple.Create("IA[ConstantProp]", new IndependentAttribute<ConstantProp>() as IAbstractDomain),
+ System.Tuple.Create("IA[Intervals]", new IndependentAttribute<Intervals>() as IAbstractDomain),
+ System.Tuple.Create("IA[PowDomain]", new IndependentAttribute<PowDomain>() as IAbstractDomain),
+ });
+
+ domains.Iter(tup => AbstractDomainFactory.Register(tup.Item1, tup.Item2));
+ program.Functions.Iter(RegisterFunction);
+ }
+
+ private static void RegisterFunction(Function func)
+ {
+ var attr = QKeyValue.FindStringAttribute(func.Attributes, "bvbuiltin");
+ if (attr != null && attr == "bvslt" && func.InParams.Count == 2 && func.InParams[0].TypedIdent.Type.IsBv)
+ bvslt.Add(func.InParams[0].TypedIdent.Type.BvBits, func);
+ }
+ }
+
+ public interface IAbstractDomain
+ {
+ IAbstractDomain Bottom();
+ IAbstractDomain MakeTop(out bool changed);
+ IAbstractDomain Join(List<Model.Element> state);
+ Expr Gamma(List<Expr> vars);
+ bool TypeCheck(List<Type> argTypes, out string msg);
+ }
+
+ public class AbstractHoudini
+ {
+ // Input Program
+ Program program;
+ // Impl -> VC
+ Dictionary<string, VCExpr> impl2VC;
+ // Impl -> Vars at end of the impl
+ Dictionary<string, List<VCExpr>> impl2EndStateVars;
+ // Impl -> (callee,summary pred)
+ Dictionary<string, List<Tuple<string, bool, VCExprVar, VCExprNAry>>> impl2CalleeSummaries;
+ // pointer to summary class
+ ISummaryElement summaryClass;
+ // impl -> summary
+ Dictionary<string, ISummaryElement> impl2Summary;
+ // name -> impl
+ Dictionary<string, Implementation> name2Impl;
+ // Use Bilateral algorithm
+ public static bool UseBilateralAlgo = true;
+ public static int iterTimeLimit = -1; // ms
+
+ public static readonly string summaryPredSuffix = "SummaryPred";
+
+ // Essentials: VCGen, Prover, and reporter
+ VCGen vcgen;
+ ProverInterface prover;
+ AbstractHoudiniErrorReporter reporter;
+
+ // Stats
+ TimeSpan proverTime;
+ int numProverQueries;
+
+ // Produce witness for correctness: can be set programmatically
+ public static string WitnessFile = "absHoudiniWitness.bpl";
+
+ public AbstractHoudini(Program program)
+ {
+ this.program = program;
+ this.impl2VC = new Dictionary<string, VCExpr>();
+ this.impl2EndStateVars = new Dictionary<string, List<VCExpr>>();
+ this.impl2CalleeSummaries = new Dictionary<string, List<Tuple<string, bool, VCExprVar, VCExprNAry>>>();
+ this.impl2Summary = new Dictionary<string, ISummaryElement>();
+ this.name2Impl = SimpleUtil.nameImplMapping(program);
+
+ if (CommandLineOptions.Clo.ProverKillTime > 0)
+ CommandLineOptions.Clo.ProverOptions = CommandLineOptions.Clo.ProverOptions.Concat1(string.Format("TIME_LIMIT={0}", CommandLineOptions.Clo.ProverKillTime));
+
+ this.vcgen = new VCGen(program, CommandLineOptions.Clo.SimplifyLogFilePath, CommandLineOptions.Clo.SimplifyLogFileAppend, new List<Checker>());
+ this.prover = ProverInterface.CreateProver(program, CommandLineOptions.Clo.SimplifyLogFilePath, CommandLineOptions.Clo.SimplifyLogFileAppend, -1);
+
+ this.reporter = new AbstractHoudiniErrorReporter();
+
+ this.proverTime = TimeSpan.Zero;
+ this.numProverQueries = 0;
+
+ if (CommandLineOptions.Clo.AbstractHoudini == "0")
+ UseBilateralAlgo = false;
+ }
+
+ public void computeSummaries(ISummaryElement summaryClass)
+ {
+ // TODO: move this some place else
+ PredicateAbs.FindUnsatPairs(prover.VCExprGen, prover);
+
+ this.summaryClass = summaryClass;
+
+ name2Impl.Values.Iter(attachEnsures);
+
+ program.Implementations
+ .Iter(impl => impl2Summary.Add(impl.Name, summaryClass.GetFlaseSummary(program, impl)));
+
+ // Build call graph
+ var Succ = new Dictionary<Implementation, HashSet<Implementation>>();
+ var Pred = new Dictionary<Implementation, HashSet<Implementation>>();
+ name2Impl.Values.Iter(impl => Succ.Add(impl, new HashSet<Implementation>()));
+ name2Impl.Values.Iter(impl => Pred.Add(impl, new HashSet<Implementation>()));
+
+ foreach (var impl in program.Implementations)
+ {
+ foreach (var blk in impl.Blocks)
+ {
+ foreach (var cmd in blk.Cmds.OfType<CallCmd>())
+ {
+ if (!name2Impl.ContainsKey(cmd.callee)) continue;
+ Succ[impl].Add(name2Impl[cmd.callee]);
+ Pred[name2Impl[cmd.callee]].Add(impl);
+ }
+ }
+ }
+
+ // Build SCC
+ var sccs = new StronglyConnectedComponents<Implementation>(name2Impl.Values,
+ new Adjacency<Implementation>(n => Pred[n]),
+ new Adjacency<Implementation>(n => Succ[n]));
+ sccs.Compute();
+
+ // impl -> priority
+ var impl2Priority = new Dictionary<string, int>();
+ int p = 0;
+ foreach (var scc in sccs)
+ {
+ foreach (var impl in scc)
+ {
+ impl2Priority.Add(impl.Name, p);
+ p++;
+ }
+ }
+
+
+ Inline();
+
+ #region Witness generation setup
+ // Create a copy of the program
+ var copy = new Dictionary<string, Implementation>();
+ if (WitnessFile != null)
+ {
+ foreach (var impl in program.Implementations)
+ {
+ var nimpl = new Implementation(Token.NoToken, impl.Name, impl.TypeParameters,
+ impl.InParams, impl.OutParams, new List<Variable>(impl.LocVars), new List<Block>());
+ foreach (var blk in impl.Blocks)
+ {
+ var cd = new CodeCopier();
+ nimpl.Blocks.Add(new Block(Token.NoToken, blk.Label,
+ cd.CopyCmdSeq(blk.Cmds), cd.CopyTransferCmd(blk.TransferCmd)));
+ }
+
+ copy.Add(impl.Name, nimpl);
+ }
+ }
+ #endregion
+
+ // Turn off subsumption. Why? Because then I see multiple occurences of the
+ // attached ensures in the VC
+ CommandLineOptions.Clo.UseSubsumption = CommandLineOptions.SubsumptionOption.Never;
+
+ // Create all VCs
+ name2Impl.Values
+ .Iter(GenVC);
+
+ // Start the iteration
+ var worklist = new SortedSet<Tuple<int, Implementation>>();
+ name2Impl.Values
+ .Iter(impl => worklist.Add(Tuple.Create(impl2Priority[impl.Name], impl)));
+
+ while (worklist.Any())
+ {
+ var impl = worklist.First().Item2;
+ worklist.Remove(worklist.First());
+
+ var changed = ProcessImpl(impl);
+
+ if (changed)
+ {
+ Pred[impl].Where(pred => UseBilateralAlgo || pred != impl).Iter(pred => worklist.Add(Tuple.Create(impl2Priority[pred.Name], pred)));
+ }
+ }
+
+ var allImpls = new SortedSet<Tuple<int, string>>();
+ name2Impl.Values.Iter(impl => allImpls.Add(Tuple.Create(impl2Priority[impl.Name], impl.Name)));
+ if (CommandLineOptions.Clo.Trace)
+ {
+ foreach (var tup in allImpls)
+ {
+ Console.WriteLine("Summary of {0}:", tup.Item2);
+ Console.WriteLine("{0}", impl2Summary[tup.Item2]);
+ }
+ Console.WriteLine("Prover time = {0}", proverTime.TotalSeconds.ToString("F2"));
+ Console.WriteLine("Number of prover queries = " + numProverQueries);
+ }
+
+ ProduceWitness(copy);
+
+ prover.Close();
+ CommandLineOptions.Clo.TheProverFactory.Close();
+ }
+
+ public HashSet<string> GetPredicates()
+ {
+ var ret = new HashSet<string>();
+ prover = ProverInterface.CreateProver(program, CommandLineOptions.Clo.SimplifyLogFilePath, CommandLineOptions.Clo.SimplifyLogFileAppend, -1);
+
+ foreach (var tup in impl2Summary)
+ {
+ var s = tup.Value as PredicateAbs;
+ if (s == null) continue;
+ ret.UnionWith(s.GetPredicates(program, prover.VCExprGen, prover));
+ // debug output
+ //Console.WriteLine("Summary of {0}:", tup.Key);
+ //Console.WriteLine("{0}", tup.Value);
+ }
+
+ prover.Close();
+ CommandLineOptions.Clo.TheProverFactory.Close();
+ return ret;
+ }
+
+ // Obtain the summary expression for a procedure: used programmatically by clients
+ // of AbstractHoudini
+ public Expr GetSummary(Program program, Procedure proc)
+ {
+ if (!impl2Summary.ContainsKey(proc.Name))
+ return Expr.True;
+
+ var vars = new Dictionary<string, Expr>();
+ foreach (var g in program.GlobalVariables)
+ vars.Add(g.Name, Expr.Ident(g));
+ foreach (var v in proc.InParams.OfType<Variable>())
+ vars.Add(v.Name, Expr.Ident(v));
+ foreach (var v in proc.OutParams.OfType<Variable>())
+ vars.Add(v.Name, Expr.Ident(v));
+
+ return impl2Summary[proc.Name].GetSummaryExpr(
+ v => { if (vars.ContainsKey(v)) return vars[v]; else return null; },
+ v => { if (vars.ContainsKey(v)) return new OldExpr(Token.NoToken, vars[v]); else return null; });
+ }
+
+ public ISummaryElement GetSummaryLowLevel(Procedure proc)
+ {
+ if (!impl2Summary.ContainsKey(proc.Name)) return null;
+ return impl2Summary[proc.Name];
+ }
+
+ // Produce a witness that proves that the inferred annotations are correct
+ private void ProduceWitness(Dictionary<string, Implementation> copy)
+ {
+ if (WitnessFile == null)
+ return;
+
+ foreach (var proc in program.Procedures)
+ {
+ var nensures = new List<Ensures>();
+ proc.Ensures.OfType<Ensures>()
+ .Where(ens => !QKeyValue.FindBoolAttribute(ens.Attributes, "ah") &&
+ !QKeyValue.FindBoolAttribute(ens.Attributes, "pre") &&
+ !QKeyValue.FindBoolAttribute(ens.Attributes, "post") &&
+ QKeyValue.FindStringAttribute(ens.Attributes, "pre") == null &&
+ QKeyValue.FindStringAttribute(ens.Attributes, "post") == null)
+ .Iter(ens => nensures.Add(ens));
+ foreach (Ensures en in nensures)
+ en.Attributes = removeAttr("InlineAssume", en.Attributes);
+
+ proc.Ensures = nensures;
+ }
+
+ var decls = new List<Declaration>(copy.Values);
+ decls.AddRange(program.TopLevelDeclarations.Where(decl => !(decl is Implementation)));
+ program.TopLevelDeclarations = decls;
+ var name2Proc = new Dictionary<string, Procedure>();
+ foreach (var proc in program.Procedures)
+ {
+ name2Proc.Add(proc.Name, proc);
+ if (impl2Summary.ContainsKey(proc.Name))
+ {
+ var ens = new Ensures(false,
+ impl2Summary[proc.Name].GetSummaryExpr(
+ new Func<string, Expr>(s => null), new Func<string, Expr>(s => null)));
+ ens.Attributes = new QKeyValue(Token.NoToken, "inferred", new List<object>(), ens.Attributes);
+ proc.Ensures.Add(ens);
+ }
+ }
+
+ using (var wt = new TokenTextWriter(WitnessFile, /*pretty=*/ false))
+ {
+ program.Emit(wt);
+ }
+
+ // Replace SummaryPreds with their definition
+ foreach (var impl in program.Implementations)
+ {
+ foreach (var blk in impl.Blocks)
+ {
+ foreach (var cmd in blk.Cmds.OfType<AssumeCmd>())
+ {
+ var expr = cmd.Expr as NAryExpr;
+ if (expr == null) continue;
+ var op = expr.Fun as FunctionCall;
+ if (op == null || !op.FunctionName.EndsWith(summaryPredSuffix)) continue;
+ var calleeName = op.FunctionName.Substring(0, op.FunctionName.Length - summaryPredSuffix.Length);
+ if (!impl2Summary.ContainsKey(calleeName)) continue;
+ var callee = name2Impl[calleeName];
+
+ // variable order: globals, ins, outs, modifies
+ var forold = new Dictionary<string, Expr>();
+ var always = new Dictionary<string, Expr>();
+ int i = 0;
+ foreach (var g in program.GlobalVariables)
+ {
+ forold.Add(g.Name, expr.Args[i]);
+ always.Add(g.Name, expr.Args[i]);
+ i++;
+ }
+ foreach (var v in callee.InParams.OfType<Variable>())
+ {
+ always.Add(v.Name, expr.Args[i]);
+ i++;
+ }
+ foreach (var v in callee.OutParams.OfType<Variable>())
+ {
+ always.Add(v.Name, expr.Args[i]);
+ i++;
+ }
+ foreach (var ie in name2Proc[calleeName].Modifies.OfType<IdentifierExpr>())
+ {
+ always[ie.Name] = expr.Args[i];
+ i++;
+ }
+
+ cmd.Expr = impl2Summary[calleeName].GetSummaryExpr(
+ v => { if (always.ContainsKey(v)) return always[v]; else return null; },
+ v => { if (forold.ContainsKey(v)) return forold[v]; else return null; });
+ }
+ }
+ }
+
+ using (var wt = new TokenTextWriter(WitnessFile, /*pretty=*/ false))
+ {
+ program.Emit(wt);
+ }
+ if (CommandLineOptions.Clo.Trace) Console.WriteLine("Witness written to {0}", WitnessFile);
+ }
+
+ private QKeyValue removeAttr(string key, QKeyValue attr)
+ {
+ if (attr == null) return attr;
+ if (attr.Key == key) return removeAttr(key, attr.Next);
+ attr.Next = removeAttr(key, attr.Next);
+ return attr;
+ }
+
+ private void Inline()
+ {
+ if (CommandLineOptions.Clo.InlineDepth < 0)
+ return;
+
+ var callGraph = BuildCallGraph();
+
+ foreach (Implementation impl in callGraph.Nodes)
+ {
+ InlineEnsuresVisitor inlineEnsuresVisitor = new InlineEnsuresVisitor();
+ inlineEnsuresVisitor.Visit(impl);
+ }
+
+ foreach (Implementation impl in callGraph.Nodes)
+ {
+ impl.OriginalBlocks = impl.Blocks;
+ impl.OriginalLocVars = impl.LocVars;
+ }
+ foreach (Implementation impl in callGraph.Nodes)
+ {
+ CommandLineOptions.Inlining savedOption = CommandLineOptions.Clo.ProcedureInlining;
+ CommandLineOptions.Clo.ProcedureInlining = CommandLineOptions.Inlining.Spec;
+ Inliner.ProcessImplementationForHoudini(program, impl);
+ CommandLineOptions.Clo.ProcedureInlining = savedOption;
+ }
+ foreach (Implementation impl in callGraph.Nodes)
+ {
+ impl.OriginalBlocks = null;
+ impl.OriginalLocVars = null;
+ }
+
+ Graph<Implementation> oldCallGraph = callGraph;
+ callGraph = new Graph<Implementation>();
+ foreach (Implementation impl in oldCallGraph.Nodes)
+ {
+ callGraph.AddSource(impl);
+ }
+ foreach (Tuple<Implementation, Implementation> edge in oldCallGraph.Edges)
+ {
+ callGraph.AddEdge(edge.Item1, edge.Item2);
+ }
+ int count = CommandLineOptions.Clo.InlineDepth;
+ while (count > 0)
+ {
+ foreach (Implementation impl in oldCallGraph.Nodes)
+ {
+ List<Implementation> newNodes = new List<Implementation>();
+ foreach (Implementation succ in callGraph.Successors(impl))
+ {
+ newNodes.AddRange(oldCallGraph.Successors(succ));
+ }
+ foreach (Implementation newNode in newNodes)
+ {
+ callGraph.AddEdge(impl, newNode);
+ }
+ }
+ count--;
+ }
+ }
+
+ private Graph<Implementation> BuildCallGraph()
+ {
+ Graph<Implementation> callGraph = new Graph<Implementation>();
+ Dictionary<Procedure, HashSet<Implementation>> procToImpls = new Dictionary<Procedure, HashSet<Implementation>>();
+ foreach (var proc in program.Procedures)
+ {
+ procToImpls[proc] = new HashSet<Implementation>();
+ }
+ foreach (var impl in program.Implementations)
+ {
+ if (impl.SkipVerification) continue;
+ callGraph.AddSource(impl);
+ procToImpls[impl.Proc].Add(impl);
+ }
+ foreach (var impl in program.Implementations)
+ {
+ if (impl.SkipVerification) continue;
+ foreach (Block b in impl.Blocks)
+ {
+ foreach (Cmd c in b.Cmds)
+ {
+ CallCmd cc = c as CallCmd;
+ if (cc == null) continue;
+ foreach (Implementation callee in procToImpls[cc.Proc])
+ {
+ callGraph.AddEdge(impl, callee);
+ }
+ }
+ }
+ }
+ return callGraph;
+ }
+
+
+ private bool ProcessImpl(Implementation impl)
+ {
+ var ret = false;
+ var gen = prover.VCExprGen;
+
+ // construct summaries
+ var env = VCExpressionGenerator.True;
+ foreach (var tup in impl2CalleeSummaries[impl.Name])
+ {
+ // Not Bilateral: then reject self predicates
+ if (UseBilateralAlgo == false && tup.Item1 == impl.Name)
+ continue;
+
+ // Bilateral: only reject self summary
+ if (UseBilateralAlgo == true && tup.Item1 == impl.Name && tup.Item2)
+ continue;
+
+ var calleeSummary =
+ impl2Summary[tup.Item1].GetSummaryExpr(
+ GetVarMapping(name2Impl[tup.Item1], tup.Item4), prover.VCExprGen);
+ env = gen.AndSimp(env, gen.Eq(tup.Item3, calleeSummary));
+ }
+
+ var prev = impl2Summary[impl.Name].Copy();
+ var upper = impl2Summary[impl.Name].GetTrueSummary(program, impl);
+ var sw = new Stopwatch();
+ sw.Start();
+ var lowerTime = TimeSpan.Zero;
+
+ while(true)
+ {
+ var usedLower = true;
+ var query = impl2Summary[impl.Name];
+ sw.Restart();
+
+ // construct self summaries
+ var summaryExpr = VCExpressionGenerator.True;
+ foreach (var tup in impl2CalleeSummaries[impl.Name])
+ {
+ if (UseBilateralAlgo == false && tup.Item1 != impl.Name)
+ continue;
+ if (UseBilateralAlgo == true && (tup.Item1 != impl.Name || !tup.Item2))
+ continue;
+
+ if (UseBilateralAlgo)
+ {
+ query = query.AbstractConsequence(upper);
+ if (query == null) query = impl2Summary[tup.Item1];
+ else usedLower = false;
+ }
+
+ var ts =
+ query.GetSummaryExpr(
+ GetVarMapping(name2Impl[tup.Item1], tup.Item4), prover.VCExprGen);
+ summaryExpr = gen.AndSimp(summaryExpr, gen.Eq(tup.Item3, ts));
+ }
+ //Console.WriteLine("Trying summary for {0}: {1}", impl.Name, summaryExpr);
+
+ reporter.model = null;
+ var vc = gen.AndSimp(env, summaryExpr);
+ vc = gen.Implies(vc, impl2VC[impl.Name]);
+
+ //Console.WriteLine("Checking: {0}", vc);
+ if (CommandLineOptions.Clo.Trace)
+ Console.WriteLine("Verifying {0} ({1}): {2}", impl.Name, usedLower ? "lower" : "ac", query);
+
+ if (usedLower && lowerTime.TotalMilliseconds >= iterTimeLimit && iterTimeLimit >= 0)
+ {
+ if (UseBilateralAlgo)
+ {
+ if (CommandLineOptions.Clo.Trace)
+ Console.WriteLine("Timeout/Spaceout while verifying " + impl.Name);
+ ret = prev.IsEqual(upper) ? false : true;
+ impl2Summary[impl.Name] = upper;
+ break;
+ }
+ else
+ {
+ if (CommandLineOptions.Clo.Trace)
+ Console.WriteLine("Timeout/Spaceout while verifying " + impl.Name);
+ var tt = impl2Summary[impl.Name].GetTrueSummary(program, impl);
+ ret = prev.IsEqual(tt) ? false : true; ;
+ impl2Summary[impl.Name] = tt;
+ break;
+ }
+ }
+
+ var start = DateTime.Now;
+
+ //prover.Push();
+ //prover.Assert(gen.Not(vc), true);
+ //prover.FlushAxiomsToTheoremProver();
+ //prover.Check();
+ //ProverInterface.Outcome proverOutcome = prover.CheckOutcome(reporter);
+ //prover.Pop();
+
+ prover.BeginCheck(impl.Name, vc, reporter);
+ ProverInterface.Outcome proverOutcome = prover.CheckOutcome(reporter);
+
+ var inc = (DateTime.Now - start);
+ proverTime += inc;
+ numProverQueries++;
+
+ sw.Stop();
+ if (usedLower) lowerTime += sw.Elapsed;
+
+ if(CommandLineOptions.Clo.Trace)
+ Console.WriteLine("Time taken = " + inc.TotalSeconds.ToString());
+
+ if (UseBilateralAlgo)
+ {
+ if (proverOutcome == ProverInterface.Outcome.TimeOut || proverOutcome == ProverInterface.Outcome.OutOfMemory)
+ {
+ if(CommandLineOptions.Clo.Trace)
+ Console.WriteLine("Timeout/Spaceout while verifying " + impl.Name);
+ ret = prev.IsEqual(upper) ? false : true;
+ impl2Summary[impl.Name] = upper;
+ break;
+ }
+
+ if (reporter.model == null && usedLower)
+ break;
+
+ if (reporter.model == null)
+ {
+ upper.Meet(query);
+ }
+ else
+ {
+ var state = CollectState(impl);
+ impl2Summary[impl.Name].Join(state, reporter.model);
+ ret = true;
+ }
+ }
+ else
+ {
+ if (proverOutcome == ProverInterface.Outcome.TimeOut || proverOutcome == ProverInterface.Outcome.OutOfMemory)
+ {
+ if (CommandLineOptions.Clo.Trace)
+ Console.WriteLine("Timeout/Spaceout while verifying " + impl.Name);
+ var tt = impl2Summary[impl.Name].GetTrueSummary(program, impl);
+ ret = prev.IsEqual(tt) ? false : true; ;
+ impl2Summary[impl.Name] = tt;
+ break;
+ }
+
+ if (reporter.model == null)
+ break;
+ //reporter.model.Write(Console.Out);
+
+ var state = CollectState(impl);
+ impl2Summary[impl.Name].Join(state, reporter.model);
+ ret = true;
+ }
+ }
+ return ret;
+ }
+
+ private Dictionary<string, VCExpr> GetVarMapping(Implementation impl, VCExprNAry summaryPred)
+ {
+ var ret = new Dictionary<string, VCExpr>();
+
+ var cnt = 0;
+ foreach (var g in program.GlobalVariables)
+ {
+ ret.Add(string.Format("old({0})", g.Name), summaryPred[cnt]);
+ cnt++;
+ }
+ foreach (var v in impl.InParams.OfType<Variable>().Concat(
+ impl.OutParams.OfType<Variable>().Concat(
+ impl.Proc.Modifies.OfType<IdentifierExpr>().Select(ie => ie.Decl))))
+ {
+ ret.Add(v.Name, summaryPred[cnt]);
+ cnt++;
+ }
+
+ // Fill up values of globals that are not modified
+ cnt = 0;
+ foreach (var g in program.GlobalVariables)
+ {
+ if (ret.ContainsKey(g.Name)) { cnt++; continue; }
+
+ ret.Add(string.Format("{0}", g.Name), summaryPred[cnt]);
+ cnt++;
+ }
+
+ // Constants
+ foreach (var c in program.Constants)
+ {
+ var value = prover.Context.BoogieExprTranslator.Translate(Expr.Ident(c));
+ ret.Add(string.Format("{0}", c.Name), value);
+ ret.Add(string.Format("old({0})", c.Name), value);
+ }
+
+ return ret;
+ }
+
+ private Dictionary<string, Model.Element> CollectState(Implementation impl)
+ {
+ var ret = new Dictionary<string, Model.Element>();
+
+ var model = reporter.model;
+ var implVars = impl2EndStateVars[impl.Name];
+
+ var cnt = 0;
+ foreach (var g in program.GlobalVariables)
+ {
+ ret.Add(string.Format("old({0})", g.Name), getValue(implVars[cnt], model));
+ cnt++;
+ }
+ foreach (var v in impl.InParams.OfType<Variable>().Concat(
+ impl.OutParams.OfType<Variable>().Concat(
+ impl.Proc.Modifies.OfType<IdentifierExpr>().Select(ie => ie.Decl))))
+ {
+ ret.Add(v.Name, getValue(implVars[cnt], model));
+ cnt++;
+ }
+
+ // Fill up values of globals that are not modified
+ cnt = 0;
+ foreach (var g in program.GlobalVariables)
+ {
+ if (ret.ContainsKey(g.Name)) { cnt++; continue; }
+
+ ret.Add(string.Format("{0}", g.Name), getValue(implVars[cnt], model));
+ cnt++;
+ }
+
+ // Constants
+ foreach (var c in program.Constants)
+ {
+ try
+ {
+ var value = getValue(prover.Context.BoogieExprTranslator.Translate(Expr.Ident(c)), model);
+ ret.Add(string.Format("{0}", c.Name), value);
+ ret.Add(string.Format("old({0})", c.Name), value);
+ }
+ catch (Exception)
+ {
+ // constant not assigned a value: add a default value
+ Model.Element value = null;
+ if (c.TypedIdent.Type.IsInt)
+ value = model.MkIntElement(0);
+ else if (c.TypedIdent.Type.IsBool)
+ value = model.MkElement("false");
+
+ ret.Add(string.Format("{0}", c.Name), value);
+ ret.Add(string.Format("old({0})", c.Name), value);
+ }
+ }
+
+ return ret;
+ }
+
+ private Model.Element getValue(VCExpr arg, Model model)
+ {
+ if (arg is VCExprLiteral)
+ {
+ //return model.GetElement(arg.ToString());
+ return model.MkElement(arg.ToString());
+ }
+ else if (arg is VCExprVar)
+ {
+ var el = model.TryGetFunc(prover.Context.Lookup(arg as VCExprVar));
+ if (el != null)
+ {
+ Debug.Assert(el.Arity == 0 && el.AppCount == 1);
+ return el.Apps.First().Result;
+ }
+ else
+ {
+ // Variable not defined; assign arbitrary value
+ if (arg.Type.IsBool)
+ return model.MkElement("false");
+ else if (arg.Type.IsInt)
+ return model.MkIntElement(0);
+ else
+ return null;
+ }
+ }
+ else
+ {
+ Debug.Assert(false);
+ return null;
+ }
+ }
+
+ private void attachEnsures(Implementation impl)
+ {
+ List<Variable> functionInterfaceVars = new List<Variable>();
+ foreach (Variable v in vcgen.program.GlobalVariables)
+ {
+ functionInterfaceVars.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", v.TypedIdent.Type), true));
+ }
+ foreach (Variable v in impl.InParams)
+ {
+ functionInterfaceVars.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", v.TypedIdent.Type), true));
+ }
+ foreach (Variable v in impl.OutParams)
+ {
+ functionInterfaceVars.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", v.TypedIdent.Type), true));
+ }
+ foreach (IdentifierExpr e in impl.Proc.Modifies)
+ {
+ if (e.Decl == null) continue;
+ functionInterfaceVars.Add(new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", e.Decl.TypedIdent.Type), true));
+ }
+ Formal returnVar = new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", Bpl.Type.Bool), false);
+ var function = new Function(Token.NoToken, impl.Name + summaryPredSuffix, functionInterfaceVars, returnVar);
+ prover.Context.DeclareFunction(function, "");
+
+ List<Expr> exprs = new List<Expr>();
+ foreach (Variable v in vcgen.program.GlobalVariables)
+ {
+ Contract.Assert(v != null);
+ exprs.Add(new OldExpr(Token.NoToken, new IdentifierExpr(Token.NoToken, v)));
+ }
+ foreach (Variable v in impl.Proc.InParams)
+ {
+ Contract.Assert(v != null);
+ exprs.Add(new IdentifierExpr(Token.NoToken, v));
+ }
+ foreach (Variable v in impl.Proc.OutParams)
+ {
+ Contract.Assert(v != null);
+ exprs.Add(new IdentifierExpr(Token.NoToken, v));
+ }
+ foreach (IdentifierExpr ie in impl.Proc.Modifies)
+ {
+ Contract.Assert(ie != null);
+ if (ie.Decl == null)
+ continue;
+ exprs.Add(ie);
+ }
+ Expr postExpr = new NAryExpr(Token.NoToken, new FunctionCall(function), exprs);
+ impl.Proc.Ensures.Add(
+ new Ensures(Token.NoToken, false, postExpr, "",
+ new QKeyValue(Token.NoToken, "ah", new List<object>(), null)));
+ }
+
+ private void GenVC(Implementation impl)
+ {
+ ModelViewInfo mvInfo;
+ Dictionary<int, Absy> label2absy;
+
+ if (CommandLineOptions.Clo.Trace)
+ {
+ Console.WriteLine("Generating VC of {0}", impl.Name);
+ }
+
+ vcgen.ConvertCFG2DAG(impl);
+ vcgen.PassifyImpl(impl, out mvInfo);
+
+ var gen = prover.VCExprGen;
+ var vcexpr = vcgen.GenerateVC(impl, null, out label2absy, prover.Context);
+
+
+ // Find the assert
+ impl2EndStateVars.Add(impl.Name, new List<VCExpr>());
+ var found = false;
+ var assertId = -1;
+ foreach (var blk in impl.Blocks)
+ {
+ foreach (var cmd in blk.Cmds.OfType<AssertCmd>())
+ {
+ if (SimpleUtil.isAssertTrue(cmd)) continue;
+ var nary = cmd.Expr as NAryExpr;
+ if (nary == null) continue;
+ var pred = nary.Fun as FunctionCall;
+ if (pred == null || pred.FunctionName != (impl.Name + (AbstractHoudini.summaryPredSuffix)))
+ continue;
+
+ Debug.Assert(!found);
+ found = true;
+ assertId = cmd.UniqueId;
+ //Console.WriteLine("assert cmd id: {0}", cmd.UniqueId);
+ nary.Args.OfType<Expr>()
+ .Iter(expr => impl2EndStateVars[impl.Name].Add(prover.Context.BoogieExprTranslator.Translate(expr)));
+ }
+ }
+
+ // It is possible that no assert is found in the procedure. It happens when the
+ // procedure doesn't return.
+ //Debug.Assert(found);
+
+ // Grab summary predicates
+ var visitor = new FindSummaryPred(prover.VCExprGen, assertId);
+ vcexpr = visitor.Mutate(vcexpr, true);
+
+ // Create a macro so that the VC can sit with the theorem prover
+ Macro macro = new Macro(Token.NoToken, impl.Name + "Macro", new List<Variable>(), new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", Bpl.Type.Bool), false));
+ prover.DefineMacro(macro, vcexpr);
+
+ // Store VC
+ impl2VC.Add(impl.Name, gen.Function(macro));
+
+ //Console.WriteLine("VC of {0}: {1}", impl.Name, vcexpr);
+
+ // check sanity: only one predicate for self-summary
+ // (There may be none when the procedure doesn't return)
+ Debug.Assert(visitor.summaryPreds.Count(tup => tup.Item2) <= 1);
+
+ impl2CalleeSummaries.Add(impl.Name, new List<Tuple<string, bool, VCExprVar, VCExprNAry>>());
+ visitor.summaryPreds.Iter(tup => impl2CalleeSummaries[impl.Name].Add(tup));
+ }
+ }
+
+ public interface ISummaryElement
+ {
+ ISummaryElement Copy();
+ ISummaryElement GetFlaseSummary(Program program, Implementation impl);
+ void Join(Dictionary<string, Model.Element> state, Model model);
+ VCExpr GetSummaryExpr(Dictionary<string, VCExpr> incarnations, VCExpressionGenerator gen);
+ Expr GetSummaryExpr(Func<string, Expr> always, Func<string, Expr> forold);
+
+ // For Bilateral
+ ISummaryElement GetTrueSummary(Program program, Implementation impl);
+ void Meet(ISummaryElement other);
+ bool IsEqual(ISummaryElement other);
+ ISummaryElement AbstractConsequence(ISummaryElement upper);
+ }
+
+ public class ConstantVal : ISummaryElement
+ {
+ Program program;
+ Implementation impl;
+ // var -> const set
+ Dictionary<string, HashSet<int>> val;
+ // set of vars
+ HashSet<Variable> vars;
+
+ public static readonly int MAX = 3;
+
+ public ConstantVal()
+ {
+ // this is just a place holder
+ val = new Dictionary<string, HashSet<int>>();
+ vars = new HashSet<Variable>();
+ }
+
+ private ConstantVal(Program program, Implementation impl)
+ {
+ this.program = program;
+ this.impl = impl;
+ this.val = new Dictionary<string, HashSet<int>>();
+
+ vars = new HashSet<Variable>();
+ impl.Proc.Modifies
+ .OfType<IdentifierExpr>()
+ .Select(ie => ie.Decl)
+ .Where(v => v.TypedIdent.Type.IsInt)
+ .Iter(v => vars.Add(v));
+ impl.OutParams.OfType<Variable>()
+ .Where(v => v.TypedIdent.Type.IsInt)
+ .Iter(v => vars.Add(v));
+
+ vars.Iter(v => val.Add(v.Name, null));
+ }
+
+
+ public void Join(Dictionary<string, Model.Element> state, Model model)
+ {
+ foreach (var vv in vars)
+ {
+ var v = vv.Name;
+ var newv = state[v].AsInt();
+ var oldv = val[v];
+
+ if (oldv == null)
+ {
+ val[v] = new HashSet<int>();
+ val[v].Add(newv);
+ }
+ else if(oldv.Count > 0)
+ {
+ val[v].Add(newv);
+ if (val[v].Count > MAX)
+ val[v] = new HashSet<int>();
+ }
+
+ }
+ }
+
+ public VCExpr GetSummaryExpr(Dictionary<string, VCExpr> incarnations, VCExpressionGenerator gen)
+ {
+ VCExpr ret = VCExpressionGenerator.True;
+ if (val.Values.Any(v => v == null))
+ return VCExpressionGenerator.False;
+
+ foreach (var v in vars)
+ {
+ var consts = val[v.Name];
+ Debug.Assert(consts != null);
+
+ if (consts.Count == 0)
+ continue;
+
+ var vexpr = VCExpressionGenerator.False;
+ consts.Iter(c => vexpr = gen.OrSimp(vexpr, gen.Eq(incarnations[v.Name], gen.Integer(Microsoft.Basetypes.BigNum.FromInt(c)))));
+ ret = gen.AndSimp(ret, vexpr);
+ }
+
+ return ret;
+ }
+
+ public override string ToString()
+ {
+ var ret = "true";
+ if (val.Values.Any(v => v == null))
+ return "false";
+
+ foreach (var v in vars)
+ {
+ var consts = val[v.Name];
+ Debug.Assert(consts != null);
+
+ if (consts.Count == 0)
+ continue;
+
+ var vexpr = "false";
+ consts.Iter(c => vexpr =
+ string.Format("{0} OR ({1} == {2})", vexpr, v.Name, c));
+
+ ret = string.Format("{0} AND ({1})", ret, vexpr);
+ }
+
+ return ret;
+ }
+
+
+ public ISummaryElement GetFlaseSummary(Program program, Implementation impl)
+ {
+ return new ConstantVal(program, impl);
+ }
+
+ #region ISummaryElement (Bilateral) Members
+
+
+ public ISummaryElement GetTrueSummary(Program program, Implementation impl)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Meet(ISummaryElement other)
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool IsEqual(ISummaryElement other)
+ {
+ throw new NotImplementedException();
+ }
+
+ public ISummaryElement AbstractConsequence(ISummaryElement upper)
+ {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+
+ #region ISummaryElement Members
+
+
+ public Expr GetSummaryExpr(Func<string, Expr> always, Func<string, Expr> forold)
+ {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+
+ #region ISummaryElement Members
+
+ public ISummaryElement Copy()
+ {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
+
+ public class NamedExpr
+ {
+ public string name;
+ public Expr expr;
+
+ public NamedExpr(string name, Expr expr)
+ {
+ this.name = name;
+ this.expr = expr;
+ }
+
+ public NamedExpr(Expr expr)
+ {
+ this.name = null;
+ this.expr = expr;
+ }
+
+ public override string ToString()
+ {
+ if (name != null)
+ return name;
+
+ return expr.ToString();
+ }
+ }
+
+ public class PredicateAbs : ISummaryElement
+ {
+ public static Dictionary<string, List<NamedExpr>> PrePreds { get; private set; }
+ public static Dictionary<string, HashSet<int>> PosPrePreds { get; private set; }
+ public static Dictionary<string, List<NamedExpr>> PostPreds { get; private set; }
+ public static Dictionary<Tuple<string, int>, List<PredicateAbsDisjunct>> UpperCandidates;
+ private static HashSet<string> boolConstants;
+ // {proc, pred-pair} -> polariry
+ public static HashSet<Tuple<string, int, int, bool, bool>> unsatPrePredPairs;
+ public static HashSet<Tuple<string, int, int, bool, bool>> unsatPostPredPairs;
+
+ // Temporary: used during eval
+ private static Model model = null;
+
+ string procName;
+ PredicateAbsDisjunct[] value;
+ bool isFalse;
+
+ public PredicateAbs(string procName)
+ {
+ this.procName = procName;
+ isFalse = true;
+ value = new PredicateAbsDisjunct[PostPreds[this.procName].Count];
+ for (int i = 0; i < PostPreds[this.procName].Count; i++) value[i] = null;
+ }
+
+ public static void Initialize(Program program)
+ {
+ PrePreds = new Dictionary<string, List<NamedExpr>>();
+ PostPreds = new Dictionary<string, List<NamedExpr>>();
+ PosPrePreds = new Dictionary<string, HashSet<int>>();
+
+ boolConstants = new HashSet<string>();
+ UpperCandidates = new Dictionary<Tuple<string, int>, List<PredicateAbsDisjunct>>();
+
+ program.Constants
+ .Where(c => c.TypedIdent.Type.IsBool)
+ .Iter(c => boolConstants.Add(c.Name));
+
+ // Add template pre-post to all procedures
+ var preT = new List<NamedExpr>();
+ var postT = new List<NamedExpr>();
+ var posPreT = new HashSet<int>();
+ var tempP = new HashSet<Procedure>();
+ foreach (var proc in
+ program.Procedures
+ .Where(proc => QKeyValue.FindBoolAttribute(proc.Attributes, "template")))
+ {
+ tempP.Add(proc);
+ foreach (var ens in proc.Ensures.OfType<Ensures>())
+ {
+ var pos = QKeyValue.FindBoolAttribute(ens.Attributes, "positive");
+
+ if (QKeyValue.FindBoolAttribute(ens.Attributes, "pre"))
+ {
+ preT.Add(new NamedExpr(null, ens.Condition));
+ if (pos) posPreT.Add(preT.Count - 1);
+ }
+
+ if (QKeyValue.FindBoolAttribute(ens.Attributes, "post"))
+ postT.Add(new NamedExpr(null, ens.Condition));
+
+ var s = QKeyValue.FindStringAttribute(ens.Attributes, "pre");
+ if (s != null)
+ {
+ preT.Add(new NamedExpr(s, ens.Condition));
+ if (pos) posPreT.Add(preT.Count - 1);
+ }
+
+ s = QKeyValue.FindStringAttribute(ens.Attributes, "post");
+ if (s != null)
+ postT.Add(new NamedExpr(s, ens.Condition));
+ }
+ }
+
+ program.RemoveTopLevelDeclarations(decl => tempP.Contains(decl));
+ var upperPreds = new Dictionary<string, List<Expr>>();
+
+ foreach (var impl in program.Implementations)
+ {
+ PrePreds.Add(impl.Name, new List<NamedExpr>());
+ PostPreds.Add(impl.Name, new List<NamedExpr>());
+ PosPrePreds.Add(impl.Name, new HashSet<int>());
+
+ // Add "false" as the first post predicate
+ //PostPreds[impl.Name].Add(new NamedExpr(Expr.False));
+
+ preT.Iter(e => PrePreds[impl.Name].Add(e));
+ postT.Iter(e => PostPreds[impl.Name].Add(e));
+ PosPrePreds[impl.Name].UnionWith(posPreT);
+
+ // Pick up per-procedure pre-post
+ var nens = new List<Ensures>();
+ foreach (var ens in impl.Proc.Ensures.OfType<Ensures>())
+ {
+ string s = null;
+ var pos = QKeyValue.FindBoolAttribute(ens.Attributes, "positive");
+
+ if (QKeyValue.FindBoolAttribute(ens.Attributes, "pre"))
+ {
+ PrePreds[impl.Name].Add(new NamedExpr(ens.Condition));
+ PosPrePreds[impl.Name].Add(PrePreds[impl.Name].Count - 1);
+ }
+ else if (QKeyValue.FindBoolAttribute(ens.Attributes, "post"))
+ {
+ PostPreds[impl.Name].Add(new NamedExpr(ens.Condition));
+ }
+ else if ((s = QKeyValue.FindStringAttribute(ens.Attributes, "pre")) != null)
+ {
+ PrePreds[impl.Name].Add(new NamedExpr(s, ens.Condition));
+ PosPrePreds[impl.Name].Add(PrePreds[impl.Name].Count - 1);
+ }
+ else if ((s = QKeyValue.FindStringAttribute(ens.Attributes, "post")) != null)
+ {
+ PostPreds[impl.Name].Add(new NamedExpr(s, ens.Condition));
+ }
+ else if (QKeyValue.FindBoolAttribute(ens.Attributes, "upper"))
+ {
+ var key = impl.Name;
+ if (!upperPreds.ContainsKey(key))
+ upperPreds.Add(key, new List<Expr>());
+ upperPreds[key].Add(ens.Condition);
+ }
+ else
+ {
+ nens.Add(ens);
+ }
+ }
+ impl.Proc.Ensures = nens;
+ }
+
+ foreach (var tup in upperPreds)
+ {
+ var procName = tup.Key;
+ var candidates = tup.Value;
+ if (!candidates.Any()) continue;
+
+ var strToPost = new Dictionary<string, int>();
+ for (int i = 0; i < PostPreds[procName].Count; i++)
+ {
+ strToPost.Add(PostPreds[procName][i].expr.ToString(), i);
+ }
+
+ foreach (var expr in candidates)
+ {
+ if (strToPost.ContainsKey(expr.ToString()))
+ {
+ var key = Tuple.Create(procName, strToPost[expr.ToString()]);
+ if (!UpperCandidates.ContainsKey(key))
+ UpperCandidates.Add(key, new List<PredicateAbsDisjunct>());
+ UpperCandidates[key].Add(new PredicateAbsDisjunct(true, procName));
+ }
+ else
+ {
+ // Try parsing the expression as (pre-conjunct ==> post-pred)
+ var parsed = ParseExpr(expr, procName);
+ if (parsed != null && strToPost.ContainsKey(parsed.Item2.ToString()))
+ {
+ var key = Tuple.Create(procName, strToPost[parsed.Item2.ToString()]);
+ if (!UpperCandidates.ContainsKey(key))
+ UpperCandidates.Add(key, new List<PredicateAbsDisjunct>());
+ UpperCandidates[key].Add(parsed.Item1);
+ }
+ }
+ }
+
+ }
+ //Console.WriteLine("Running Abstract Houdini");
+ //PostPreds.Iter(expr => Console.WriteLine("\tPost: {0}", expr));
+ //PrePreds.Iter(expr => Console.WriteLine("\tPre: {0}", expr));
+ }
+
+ // Try parsing the expression as (pre-conjunct ==> post-pred)
+ private static Tuple<PredicateAbsDisjunct, Expr> ParseExpr(Expr expr, string procName)
+ {
+ Expr postExpr = null;
+ Expr preExpr = null;
+
+ // Decompose outer Implies
+ var nexpr = expr as NAryExpr;
+ if (nexpr != null && (nexpr.Fun is BinaryOperator)
+ && (nexpr.Fun as BinaryOperator).Op == BinaryOperator.Opcode.Imp
+ && (nexpr.Args.Count == 2))
+ {
+ postExpr = nexpr.Args[1];
+ preExpr = nexpr.Args[0];
+ }
+ else
+ {
+ if(CommandLineOptions.Clo.Trace) Console.WriteLine("Failed to parse {0} (ignoring)", expr);
+ return null;
+ }
+
+
+ var atoms = DecomposeOuterAnd(preExpr);
+ var pos = new HashSet<int>();
+ var neg = new HashSet<int>();
+
+ foreach (var atom in atoms)
+ {
+ var index = PrePreds[procName].FindIndex(ne => ne.expr.ToString() == atom.ToString());
+ if (index == -1)
+ {
+ index = PrePreds[procName].FindIndex(ne => Expr.Not(ne.expr).ToString() == atom.ToString());
+ if (index == -1)
+ {
+ if(CommandLineOptions.Clo.Trace) Console.WriteLine("Failed to parse {0} (ignoring)", atom);
+ return null;
+ }
+ else
+ {
+ neg.Add(index);
+ }
+ }
+ else
+ {
+ pos.Add(index);
+ }
+ }
+
+ var conj = new PredicateAbsConjunct(pos, neg, procName);
+ var conjls = new List<PredicateAbsConjunct>();
+ conjls.Add(conj);
+
+ return Tuple.Create(new PredicateAbsDisjunct(conjls, procName), postExpr);
+ }
+
+ // blah && blah ==> {blah, blah}
+ static IEnumerable<Expr> DecomposeOuterAnd(Expr expr)
+ {
+ var ret = new List<Expr>();
+
+ var nexpr = expr as NAryExpr;
+ if (nexpr == null
+ || !(nexpr.Fun is BinaryOperator)
+ || (nexpr.Fun as BinaryOperator).Op != BinaryOperator.Opcode.And)
+ {
+ ret.Add(expr);
+ }
+ else
+ {
+ foreach (Expr a in nexpr.Args)
+ ret.AddRange(DecomposeOuterAnd(a));
+ }
+
+ return ret;
+ }
+
+
+ private Model.Element Eval(Expr expr, Dictionary<string, Model.Element> state)
+ {
+ if (expr is LiteralExpr)
+ {
+ return ToElem((expr as LiteralExpr).Val);
+ }
+
+ if (expr is IdentifierExpr)
+ {
+ return LookupVariable((expr as IdentifierExpr).Name, state, false);
+ }
+
+ if (expr is OldExpr)
+ {
+ var ide = (expr as OldExpr).Expr as IdentifierExpr;
+ Debug.Assert(ide != null);
+
+ return LookupVariable(ide.Name, state, true);
+ }
+
+ if (expr is NAryExpr)
+ {
+ var nary = expr as NAryExpr;
+ if (nary.Fun is UnaryOperator)
+ {
+ return ToElem((nary.Fun as UnaryOperator).Evaluate(ToValue(Eval(nary.Args[0], state))));
+ }
+ if (nary.Fun is BinaryOperator)
+ {
+ return ToElem((nary.Fun as BinaryOperator).Evaluate(ToValue(Eval(nary.Args[0], state)), ToValue(Eval(nary.Args[1], state))));
+ }
+ if (nary.Fun is MapSelect && nary.Args.Count == 2)
+ {
+ var index = Eval(nary.Args[1], state);
+ var map = Eval(nary.Args[0], state) as Model.Array;
+ Debug.Assert(map != null, "Variable of map type must have an Array-typed value");
+ var ret = map.Value.TryEval(index as Model.Element);
+ if (ret == null) ret = map.Value.Else;
+ Debug.Assert(ret != null);
+ return ret;
+ }
+ Debug.Assert(false, "No other op is handled");
+ }
+ throw new NotImplementedException(string.Format("Expr of type {0} is not handled", expr.GetType().ToString()));
+ }
+
+ private Model.Element LookupVariable(string v, Dictionary<string, Model.Element> state, bool tryOld)
+ {
+ if (tryOld)
+ {
+ var oldv = string.Format("old({0})", v);
+ if (state.ContainsKey(oldv))
+ {
+ return state[oldv];
+ }
+ throw new AbsHoudiniInternalError("Cannot handle this case");
+ }
+
+ if (state.ContainsKey(v))
+ {
+ return state[v];
+ }
+
+ /*
+ if (boolConstants.Contains(v))
+ {
+ // value of this constant is immaterial, return true
+ return model.MkElement("true");
+ }
+ */
+
+ throw new AbsHoudiniInternalError("Cannot handle this case");
+ }
+
+ private static VCExpr ToVcVar(string v, Dictionary<string, VCExpr> incarnations, bool tryOld)
+ {
+ if (tryOld)
+ {
+ var oldv = string.Format("old({0})", v);
+ if (incarnations.ContainsKey(oldv))
+ {
+ return incarnations[oldv];
+ }
+ throw new AbsHoudiniInternalError("Cannot handle this case");
+ }
+
+ if (incarnations.ContainsKey(v))
+ {
+ return incarnations[v];
+ }
+
+ throw new AbsHoudiniInternalError("Cannot handle this case");
+ }
+
+ public static void FindUnsatPairs(VCExpressionGenerator gen, ProverInterface prover)
+ {
+ unsatPrePredPairs = new HashSet<Tuple<string, int, int, bool, bool>>();
+ unsatPostPredPairs = new HashSet<Tuple<string, int, int, bool, bool>>();
+
+ var cachePos = new HashSet<Tuple<string, string>>();
+ var cacheNeg = new HashSet<Tuple<string, string>>();
+ var record = new Action<object, string, int, int, bool, bool>(
+ (map, proc, e1, e2, p1, p2) => {
+ var key = Tuple.Create(proc, e1, e2, p1, p2);
+ if (map == PrePreds)
+ unsatPrePredPairs.Add(key);
+ else
+ unsatPostPredPairs.Add(key);
+ }
+ );
+
+ var predMaps = new List<Dictionary<string, List<NamedExpr>>>();
+ predMaps.Add(PrePreds); predMaps.Add(PostPreds);
+
+ foreach (var map in predMaps)
+ {
+ foreach (var proc in map.Keys)
+ {
+ for (int i = 0; i < 2 * map[proc].Count(); i++)
+ {
+ var p1 = (i % 2); // polarity
+ var e1 = map[proc][i / 2].expr;
+ if (p1 == 0) e1 = Expr.Not(e1);
+
+ for (int j = 2 * ((i / 2) + 1); j < 2 * map[proc].Count(); j++)
+ {
+ var p2 = (j % 2); // polarity
+ var e2 = map[proc][j / 2].expr;
+ if (p2 == 0) e2 = Expr.Not(e2);
+
+ var key = Tuple.Create(e1.ToString(), e2.ToString());
+ if (cachePos.Contains(key))
+ {
+ record(map, proc, i / 2, j / 2, p1 == 1, p2 == 1);
+ continue;
+ }
+ else if (cacheNeg.Contains(key))
+ {
+ continue;
+ }
+
+ if (!CheckIfUnsat(e1, e2, gen, prover))
+ {
+ cacheNeg.Add(key);
+ continue;
+ }
+ cachePos.Add(key);
+ record(map, proc, i / 2, j / 2, p1 == 1, p2 == 1);
+
+ if (CommandLineOptions.Clo.Trace)
+ Console.WriteLine("Proved UNSAT: {0} {1}", e1, e2);
+ }
+ }
+ }
+ }
+ }
+
+ // Is a ^ b UNSAT?
+ private static bool CheckIfUnsat(Expr a, Expr b, VCExpressionGenerator gen, ProverInterface prover)
+ {
+ var gatherLitA = new GatherLiterals();
+ var gatherLitB = new GatherLiterals();
+
+ gatherLitA.Visit(a);
+ gatherLitB.Visit(b);
+
+ var seta = new HashSet<Variable>();
+ var setb = new HashSet<Variable>();
+ gatherLitA.literals.Iter(tup => seta.Add(tup.Item1));
+ gatherLitB.literals.Iter(tup => setb.Add(tup.Item1));
+ seta.IntersectWith(setb);
+ if (!seta.Any()) return false;
+
+ // Create fresh variables
+ return CheckIfUnsat(Expr.And(a, b), gen, prover);
+ }
+
+ // Is a UNSAT?
+ private static bool CheckIfUnsat(Expr a, VCExpressionGenerator gen, ProverInterface prover)
+ {
+ var gatherLitA = new GatherLiterals();
+ gatherLitA.Visit(a);
+
+ // Create fresh variables
+ var counter = 0;
+ var incarnations = new Dictionary<string, VCExpr>();
+ foreach (var literal in gatherLitA.literals)
+ {
+ if (incarnations.ContainsKey(literal.Item2.ToString()))
+ continue;
+
+ //if(!literal.Item1.TypedIdent.Type.IsInt && !literal.Item1.TypedIdent.Type.IsBool)
+ var v = gen.Variable("UNSATCheck" + counter, literal.Item1.TypedIdent.Type);
+ incarnations.Add(literal.Item2.ToString(), v);
+ counter++;
+ }
+
+ var vc1 = ToVcExpr(a, incarnations, gen);
+ var vc = gen.LabelPos("Temp", vc1);
+
+ // check
+ prover.AssertAxioms();
+ prover.Push();
+ prover.Assert(vc, true);
+ prover.Check();
+ var outcome = prover.CheckOutcomeCore(new AbstractHoudiniErrorReporter());
+ prover.Pop();
+
+ if (outcome == ProverInterface.Outcome.Valid)
+ return true;
+ return false;
+ }
+
+
+ class GatherLiterals : ReadOnlyVisitor
+ {
+ public List<Tuple<Variable, Expr>> literals;
+ bool inOld;
+
+ public GatherLiterals()
+ {
+ literals = new List<Tuple<Variable, Expr>>();
+ inOld = false;
+ }
+
+ public override Expr VisitOldExpr(OldExpr node)
+ {
+ var prev = inOld;
+ inOld = true;
+ var ret = base.VisitOldExpr(node);
+ inOld = prev;
+ return ret;
+ }
+
+ public override Expr VisitIdentifierExpr(IdentifierExpr node)
+ {
+ if (inOld)
+ literals.Add(Tuple.Create(node.Decl, new OldExpr(Token.NoToken, node) as Expr));
+ else
+ literals.Add(Tuple.Create(node.Decl, node as Expr));
+
+ return node;
+ }
+ }
+
+ private object ToValue(Model.Element elem)
+ {
+ if (elem is Model.Integer)
+ {
+ return Microsoft.Basetypes.BigNum.FromInt((elem as Model.Integer).AsInt());
+ }
+ if (elem is Model.Boolean)
+ {
+ return (elem as Model.Boolean).Value;
+ }
+ if (elem is Model.DatatypeValue && (elem as Model.DatatypeValue).Arguments.Length == 1 &&
+ (elem as Model.DatatypeValue).ConstructorName == "-" &&
+ (elem as Model.DatatypeValue).Arguments[0] is Model.Integer)
+ {
+ // negative number as "-" @ int
+ return Microsoft.Basetypes.BigNum.FromInt(-1 * ((elem as Model.DatatypeValue).Arguments[0] as Model.Integer).AsInt());
+ }
+ throw new NotImplementedException("Cannot yet handle this Model.Element type");
+ }
+
+ private Model.Element ToElem(object val)
+ {
+ if (val is bool || val is int || val is Basetypes.BigNum)
+ return model.MkElement(val.ToString());
+ throw new NotImplementedException("Cannot yet handle this value type");
+ }
+
+ // replace v by old(v)
+ private static Expr MakeOld(Expr expr)
+ {
+ var substalways = new Substitution(v => new OldExpr(Token.NoToken, Expr.Ident(v)));
+ var substold = new Substitution(v => Expr.Ident(v));
+
+ return Substituter.ApplyReplacingOldExprs(substalways, substold, expr);
+ }
+
+ private static Expr ToExpr(Expr expr, Func<string, Expr> always, Func<string, Expr> forold)
+ {
+ var substalways = new Substitution(v =>
+ {
+ var ret = always(v.Name);
+ if (ret != null) return ret;
+ else return Expr.Ident(v);
+ });
+ var substold = new Substitution(v =>
+ {
+ var ret = forold(v.Name);
+ if (ret != null) return ret;
+ else return new OldExpr(Token.NoToken, Expr.Ident(v));
+ });
+
+ return Substituter.ApplyReplacingOldExprs(substalways, substold, expr);
+ }
+
+ private static VCExpr ToVcExpr(Expr expr, Dictionary<string, VCExpr> incarnations, VCExpressionGenerator gen)
+ {
+ if (expr is LiteralExpr)
+ {
+ var val = (expr as LiteralExpr).Val;
+ if (val is bool)
+ {
+ if ((bool)val)
+ {
+ return VCExpressionGenerator.True;
+ }
+ else
+ {
+ return VCExpressionGenerator.False;
+ }
+ }
+ else if (val is Microsoft.Basetypes.BigNum)
+ {
+ return gen.Integer((Microsoft.Basetypes.BigNum)val);
+ }
+
+ throw new NotImplementedException("Cannot handle literals of this type");
+ }
+
+ if (expr is IdentifierExpr)
+ {
+ return ToVcVar((expr as IdentifierExpr).Name, incarnations, false);
+ }
+
+ if (expr is OldExpr)
+ {
+ var ide = (expr as OldExpr).Expr as IdentifierExpr;
+ Debug.Assert(ide != null);
+
+ return ToVcVar(ide.Name, incarnations, true);
+ }
+
+ if (expr is NAryExpr)
+ {
+ var nary = expr as NAryExpr;
+ if (nary.Fun is UnaryOperator)
+ {
+ if ((nary.Fun as UnaryOperator).Op == UnaryOperator.Opcode.Not)
+ return gen.Not(ToVcExpr(nary.Args[0], incarnations, gen));
+ else if ((nary.Fun as UnaryOperator).Op == UnaryOperator.Opcode.Neg)
+ return gen.Function(VCExpressionGenerator.SubIOp, gen.Integer(Basetypes.BigNum.FromInt(0)), ToVcExpr(nary.Args[0], incarnations, gen));
+ else
+ Debug.Assert(false, "No other unary op is handled");
+ }
+ if (nary.Fun is BinaryOperator)
+ {
+ return gen.Function(Translate(nary.Fun as BinaryOperator), ToVcExpr(nary.Args[0], incarnations, gen), ToVcExpr(nary.Args[1], incarnations, gen));
+ }
+ if (nary.Fun is MapSelect && nary.Args.Count == 2)
+ {
+ return gen.Select(ToVcExpr(nary.Args[0], incarnations, gen), ToVcExpr(nary.Args[1], incarnations, gen));
+ }
+ Debug.Assert(false, "No other op is handled");
+ }
+ throw new NotImplementedException(string.Format("Expr of type {0} is not handled", expr.GetType().ToString()));
+ }
+
+ private static VCExprOp Translate(BinaryOperator op)
+ {
+ switch (op.Op)
+ {
+ case BinaryOperator.Opcode.Add:
+ return VCExpressionGenerator.AddIOp;
+ case BinaryOperator.Opcode.Sub:
+ return VCExpressionGenerator.SubIOp;
+ case BinaryOperator.Opcode.Mul:
+ return VCExpressionGenerator.MulIOp;
+ case BinaryOperator.Opcode.Div:
+ return VCExpressionGenerator.DivIOp;
+ case BinaryOperator.Opcode.Mod:
+ return VCExpressionGenerator.ModOp;
+ case BinaryOperator.Opcode.Eq:
+ case BinaryOperator.Opcode.Iff:
+ // we don't distinguish between equality and equivalence at this point
+ return VCExpressionGenerator.EqOp;
+ case BinaryOperator.Opcode.Neq:
+ return VCExpressionGenerator.NeqOp;
+ case BinaryOperator.Opcode.Lt:
+ return VCExpressionGenerator.LtOp;
+ case BinaryOperator.Opcode.Le:
+ return VCExpressionGenerator.LeOp;
+ case BinaryOperator.Opcode.Ge:
+ return VCExpressionGenerator.GeOp;
+ case BinaryOperator.Opcode.Gt:
+ return VCExpressionGenerator.GtOp;
+ case BinaryOperator.Opcode.Imp:
+ return VCExpressionGenerator.ImpliesOp;
+ case BinaryOperator.Opcode.And:
+ return VCExpressionGenerator.AndOp;
+ case BinaryOperator.Opcode.Or:
+ return VCExpressionGenerator.OrOp;
+ case BinaryOperator.Opcode.Subtype:
+ return VCExpressionGenerator.SubtypeOp;
+ default:
+ Contract.Assert(false);
+ throw new NotImplementedException();
+ }
+
+ }
+
+ // If "false" is a post-predicate, then remove its "pre" constraint from all others, whenever possible
+ public void Simplify()
+ {
+ // find "false"
+ var findex = PostPreds[procName].FindIndex(ne => (ne.expr is LiteralExpr) && (ne.expr as LiteralExpr).IsFalse);
+ if (findex < 0) return;
+ if (value[findex].isTrue)
+ {
+ // procedure doesn't return
+ for (int i = 0; i < value.Length; i++)
+ if (i != findex) value[i] = new PredicateAbsDisjunct(false, procName);
+ return;
+ }
+ if (value[findex].isFalse)
+ return;
+
+ for (int i = 0; i < value.Length; i++)
+ if (i != findex) value[i].Subtract(value[findex]);
+ }
+
+ public HashSet<string> GetPredicates(Program program, VCExpressionGenerator gen, ProverInterface prover)
+ {
+ var ret = new HashSet<string>();
+ if (isFalse) return ret;
+ Simplify();
+
+ // Find the free expressions
+ var proc = program.Procedures.FirstOrDefault(p => p.Name == procName);
+ Contract.Assert(proc != null);
+ Expr freeSummary = Expr.True;
+ foreach (var req in proc.Requires.OfType<Requires>().Where(req => req.Free))
+ {
+ freeSummary = Expr.And(freeSummary, MakeOld(req.Condition));
+ }
+ foreach (var ens in proc.Ensures.OfType<Ensures>().Where(ens => ens.Free))
+ {
+ freeSummary = Expr.And(freeSummary, ens.Condition);
+ }
+
+ for (int i = 0; i < PostPreds[procName].Count; i++)
+ {
+ if (value[i].isFalse) continue;
+ if (PostPreds[procName][i].expr is LiteralExpr && (PostPreds[procName][i].expr as LiteralExpr).IsFalse)
+ continue;
+
+ if (value[i].isTrue)
+ ret.Add(PostPreds[procName][i].expr.ToString());
+ else
+ {
+ foreach (var c in value[i].GetConjuncts())
+ {
+ var s = Expr.Imp(c.ToExpr(j => PrePreds[procName][j].expr), PostPreds[procName][i].expr);
+ if (CheckIfUnsat(Expr.And(freeSummary, Expr.Not(s)), gen, prover))
+ continue;
+ ret.Add(s.ToString());
+ }
+ }
+ }
+ return ret;
+ }
+
+ public override string ToString()
+ {
+ var ret = "";
+ if (isFalse) return "false";
+ var first = true;
+
+ for(int i = 0; i < PostPreds[procName].Count; i++)
+ {
+ if(value[i].isFalse) continue;
+
+ if(value[i].isTrue)
+ ret += string.Format("{0}{1}", first ? "" : " && ", PostPreds[procName][i]);
+ else
+ ret += string.Format("{0}({1} ==> {2})", first ? "" : " && ", value[i], PostPreds[procName][i]);
+
+ first = false;
+ }
+ if (ret == "") ret = "true";
+ return ret;
+ }
+
+
+ #region ISummaryElement Members
+
+ public ISummaryElement Copy()
+ {
+ var ret = new PredicateAbs(procName);
+ ret.isFalse = isFalse;
+ ret.value = new PredicateAbsDisjunct[value.Length];
+ for (int i = 0; i < value.Length; i++)
+ ret.value[i] = value[i];
+ return ret;
+ }
+
+ public ISummaryElement GetFlaseSummary(Program program, Implementation impl)
+ {
+ return new PredicateAbs(impl.Name);
+ }
+
+ public ISummaryElement GetTrueSummary(Program program, Implementation impl)
+ {
+ var ret = new PredicateAbs(impl.Name);
+ ret.isFalse = false;
+ for (int i = 0; i < PostPreds[this.procName].Count; i++) ret.value[i] = new PredicateAbsDisjunct(false, impl.Name);
+
+ return ret;
+ }
+
+ public void Join(Dictionary<string, Model.Element> state, Model model)
+ {
+ PredicateAbs.model = model;
+
+ // Evaluate each predicate on the state
+ var prePredsVal = new bool[PrePreds[procName].Count];
+ var postPredsVal = new bool[PostPreds[procName].Count];
+
+ var indexSeq = new List<int>();
+ for (int i = 0; i < PrePreds[procName].Count; i++) indexSeq.Add(i);
+
+ for (int i = 0; i < PrePreds[procName].Count; i++)
+ {
+ var v = ToValue(Eval(PrePreds[procName][i].expr, state));
+ Debug.Assert(v is bool);
+ prePredsVal[i] = (bool)v;
+ }
+
+ for (int i = 0; i < PostPreds[procName].Count; i++)
+ {
+ var v = ToValue(Eval(PostPreds[procName][i].expr, state));
+ Debug.Assert(v is bool);
+ postPredsVal[i] = (bool)v;
+ }
+
+ for (int i = 0; i < PostPreds[procName].Count; i++)
+ {
+ // No hope for this post pred?
+ if (!isFalse && value[i].isFalse) continue;
+
+ var newDisj = new PredicateAbsDisjunct(true, procName);
+ if (!postPredsVal[i])
+ {
+ newDisj = new PredicateAbsDisjunct(indexSeq.Where(j => !prePredsVal[j]), indexSeq.Where(j => prePredsVal[j] && !PosPrePreds[procName].Contains(j)), procName);
+ }
+
+ if (isFalse)
+ value[i] = newDisj;
+ else
+ value[i] = PredicateAbsDisjunct.And(value[i], newDisj);
+ }
+
+ /*
+ // do beta(model)
+ var that = new PredicateAbsDisjunct[PostPreds[procName].Count];
+ for (int i = 0; i < PostPreds[procName].Count; i++)
+ {
+ if (postPredsVal[i])
+ that[i] = new PredicateAbsDisjunct(true, procName);
+ else if (i == 0)
+ {
+ Debug.Assert(PostPreds[procName][0].ToString() == "false");
+ var newDisj = new PredicateAbsDisjunct(true, procName);
+ newDisj = new PredicateAbsDisjunct(indexSeq.Where(j => !prePredsVal[j]), indexSeq.Where(j => prePredsVal[j]), procName);
+ that[i] = newDisj;
+ }
+ else
+ {
+ // false
+ that[i] = new PredicateAbsDisjunct(false, procName);
+ }
+ }
+
+ // Do join
+ for (int i = 0; i < PostPreds[procName].Count; i++)
+ {
+ if (isFalse)
+ value[i] = that[i];
+ else
+ {
+ if (i == 0)
+ value[i] = PredicateAbsDisjunct.And(value[i], that[i]);
+ else
+ {
+ var c1 = PredicateAbsDisjunct.And(value[i], that[i]);
+ var c2 = PredicateAbsDisjunct.And(value[i], that[0]);
+ var c3 = PredicateAbsDisjunct.And(value[0], that[i]);
+ value[i] = PredicateAbsDisjunct.Or(c1, c2);
+ value[i] = PredicateAbsDisjunct.Or(value[i], c3);
+ }
+ }
+ }
+ */
+ isFalse = false;
+
+ //Console.WriteLine("Result of Join: {0}", this.ToString());
+ }
+
+ // Precondition: the upper guys are just {true/false/upper-candidate} ==> post-pred
+ public void Meet(ISummaryElement iother)
+ {
+ var other = iother as PredicateAbs;
+ if (isFalse) return;
+ if (other.isFalse)
+ {
+ isFalse = true;
+ for (int i = 0; i < PostPreds[this.procName].Count; i++) value[i] = null;
+ return;
+ }
+ Debug.Assert(this.procName == other.procName);
+
+ for (int i = 0; i < PostPreds[this.procName].Count; i++)
+ {
+ value[i] = PredicateAbsDisjunct.Or(value[i], other.value[i]);
+ }
+ }
+
+ public bool IsEqual(ISummaryElement other)
+ {
+ var that = other as PredicateAbs;
+ if (isFalse && that.isFalse) return true;
+ if (isFalse || that.isFalse) return false;
+ for (int i = 0; i < PostPreds[procName].Count; i++)
+ {
+ if (!PredicateAbsDisjunct.syntacticLessThan(value[i], that.value[i]) ||
+ !PredicateAbsDisjunct.syntacticLessThan(that.value[i], value[i]))
+ return false;
+ }
+ return true;
+ }
+
+ // Precondition: the upper guys are just {true/false/upper-candidate} ==> post-pred
+ // Postcondition: the returned value is also of this form (for just one post-pred)
+ public ISummaryElement AbstractConsequence(ISummaryElement iupper)
+ {
+ var upper = iupper as PredicateAbs;
+
+ for (int i = 0; i < PostPreds[this.procName].Count; i++)
+ {
+ if (upper.value[i].isTrue) continue;
+ if (!UpperCandidates.ContainsKey(Tuple.Create(procName, i))) continue;
+
+ foreach (var candidate in UpperCandidates[Tuple.Create(procName, i)])
+ {
+ if (PredicateAbsDisjunct.syntacticLessThan(candidate, upper.value[i]))
+ continue;
+ if (!this.isFalse && !PredicateAbsDisjunct.syntacticLessThan(candidate, this.value[i]))
+ continue;
+
+ var ret = new PredicateAbs(this.procName);
+ ret.isFalse = false;
+ for (int j = 0; j < PostPreds[this.procName].Count; j++)
+ ret.value[j] = new PredicateAbsDisjunct(false, this.procName);
+
+ ret.value[i] = candidate;
+
+ return ret;
+ }
+ }
+
+ // Giveup: the abstract consequence is too difficult to compute
+ return null;
+ }
+
+ public VCExpr GetSummaryExpr(Dictionary<string, VCExpr> incarnations, VCExpressionGenerator gen)
+ {
+ if (isFalse)
+ return VCExpressionGenerator.False;
+
+ var ret = VCExpressionGenerator.True;
+
+ for(int i = 0; i < PostPreds[procName].Count; i++)
+ {
+ ret = gen.AndSimp(ret, gen.ImpliesSimp(value[i].ToVcExpr(j => ToVcExpr(PrePreds[procName][j].expr, incarnations, gen), gen), ToVcExpr(PostPreds[procName][i].expr, incarnations, gen)));
+ }
+
+ return ret;
+ }
+
+ public Expr GetSummaryExpr(Func<string, Expr> always, Func<string, Expr> forold)
+ {
+ if (isFalse)
+ return Expr.False;
+
+ Expr ret = Expr.True;
+
+ for (int i = 0; i < PostPreds[procName].Count; i++)
+ {
+ ret = Expr.And(ret, Expr.Imp(value[i].ToExpr(j => ToExpr(PrePreds[procName][j].expr, always, forold)), ToExpr(PostPreds[procName][i].expr, always, forold)));
+ }
+
+ return ret;
+ }
+
+ #endregion
+ }
+
+ public class PredicateAbsDisjunct
+ {
+ List<PredicateAbsConjunct> conjuncts;
+ string ProcName;
+ public bool isTrue {get; private set;}
+ public bool isFalse
+ {
+ get
+ {
+ if (isTrue) return false;
+ return conjuncts.Count == 0;
+ }
+ }
+
+ public PredicateAbsDisjunct(bool isTrue, string ProcName)
+ {
+ this.isTrue = isTrue;
+ this.ProcName = ProcName;
+ conjuncts = new List<PredicateAbsConjunct>();
+ }
+
+ public PredicateAbsDisjunct(List<PredicateAbsConjunct> conjuncts, string ProcName)
+ {
+ isTrue = false;
+ this.conjuncts = conjuncts;
+ this.ProcName = ProcName;
+ }
+
+ // Disjunct of singleton conjuncts
+ public PredicateAbsDisjunct(IEnumerable<int> pos, IEnumerable<int> neg, string ProcName)
+ {
+ this.ProcName = ProcName;
+ conjuncts = new List<PredicateAbsConjunct>();
+ isTrue = false;
+ pos.Iter(p => conjuncts.Add(PredicateAbsConjunct.Singleton(p, true, ProcName)));
+ neg.Iter(p => conjuncts.Add(PredicateAbsConjunct.Singleton(p, false, ProcName)));
+ }
+
+ // Does d1 describe a smaller set of states than d2? This is true when every conjunct of d1
+ // is smaller than some conjunct of d2
+ public static bool syntacticLessThan(PredicateAbsDisjunct d1, PredicateAbsDisjunct d2)
+ {
+ if (d2.isTrue) return true;
+ if (d1.isTrue) return false;
+ if (d1.isFalse) return true;
+ if (d2.isFalse) return false;
+
+ foreach (var c1 in d1.conjuncts)
+ {
+ if (d2.conjuncts.Any(c2 => PredicateAbsConjunct.syntacticLessThan(c1, c2)))
+ continue;
+ else
+ return false;
+ }
+ return true;
+ }
+
+ public static PredicateAbsDisjunct And(PredicateAbsDisjunct v1, PredicateAbsDisjunct v2)
+ {
+ if (v1.isTrue) return v2;
+ if (v2.isTrue) return v1;
+
+ var result = new List<PredicateAbsConjunct>();
+
+ foreach (var c1 in v1.conjuncts)
+ {
+ foreach (var c2 in v2.conjuncts)
+ {
+ var c = PredicateAbsConjunct.And(c1, c2);
+ if (c.isFalse) continue;
+ if (result.Any(cprime => c.implies(cprime))) continue;
+ var tmp = new List<PredicateAbsConjunct>();
+ tmp.Add(c);
+ result.Where(cprime => !cprime.implies(c)).Iter(cprime => tmp.Add(cprime));
+ result = tmp;
+ }
+ }
+
+ return new PredicateAbsDisjunct(result, v1.ProcName);
+ }
+
+ public static PredicateAbsDisjunct Or(PredicateAbsDisjunct v1, PredicateAbsDisjunct v2)
+ {
+ if (v1.isTrue) return v1;
+ if (v2.isTrue) return v2;
+ if (v1.isFalse) return v2;
+ if (v2.isFalse) return v1;
+
+ var result = new List<PredicateAbsConjunct>();
+ v1.conjuncts.Iter(c => result.Add(c));
+
+ foreach (var c in v2.conjuncts)
+ {
+ if (result.Any(cprime => c.implies(cprime))) continue;
+ var tmp = new List<PredicateAbsConjunct>();
+ tmp.Add(c);
+ result.Where(cprime => !cprime.implies(c)).Iter(cprime => tmp.Add(cprime));
+ result = tmp;
+ }
+
+ return new PredicateAbsDisjunct(result, v1.ProcName);
+ }
+
+ public VCExpr ToVcExpr(Func<int, VCExpr> predToExpr, VCExpressionGenerator gen)
+ {
+ if (isTrue) return VCExpressionGenerator.True;
+ var ret = VCExpressionGenerator.False;
+ conjuncts.Iter(c => ret = gen.OrSimp(ret, c.ToVcExpr(predToExpr, gen)));
+ return ret;
+ }
+
+ public Expr ToExpr(Func<int, Expr> predToExpr)
+ {
+ if (isTrue) return Expr.True;
+ Expr ret = Expr.False;
+ conjuncts.Iter(c => ret = Expr.Or(ret, c.ToExpr(predToExpr)));
+ return ret;
+ }
+
+ public override string ToString()
+ {
+ if(isTrue)
+ return "true";
+ var ret = "";
+ var first = true;
+ foreach (var c in conjuncts)
+ {
+ if (c.isFalse) continue;
+ ret += string.Format("{0}{1}", first ? "" : " || ", c);
+ first = false;
+ }
+ return ret;
+ }
+
+ public void Subtract(PredicateAbsDisjunct that)
+ {
+ var ncon = new List<PredicateAbsConjunct>();
+ foreach (var c1 in conjuncts)
+ {
+ if (that.conjuncts.Any(c2 => c1.implies(c2)))
+ continue;
+ ncon.Add(c1);
+ }
+ conjuncts = ncon;
+ }
+
+ public IEnumerable<PredicateAbsConjunct> GetConjuncts()
+ {
+ return conjuncts;
+ }
+
+ }
+
+ public class PredicateAbsConjunct
+ {
+ static int ConjunctBound = 3;
+
+ public bool isFalse { get; private set; }
+ HashSet<int> posPreds;
+ HashSet<int> negPreds;
+ string ProcName;
+
+ public static void Initialize(int bound)
+ {
+ ConjunctBound = bound;
+ }
+
+ private void Normalize()
+ {
+ if (posPreds.Intersect(negPreds).Any() || negPreds.Intersect(posPreds).Any() || (posPreds.Count + negPreds.Count > ConjunctBound))
+ {
+ isFalse = true;
+ posPreds = new HashSet<int>();
+ negPreds = new HashSet<int>();
+ }
+ }
+
+ // Do this step only once in a while?
+ private void StrongNormalize()
+ {
+ if (isFalse) return;
+
+ var candidates = new List<Tuple<int, bool>>();
+ posPreds.Iter(p => candidates.Add(Tuple.Create(p, true)));
+ negPreds.Iter(p => candidates.Add(Tuple.Create(p, false)));
+ var drop = new HashSet<int>();
+ for (int i = 0; i < candidates.Count; i++)
+ {
+ for (int j = 0; j < candidates.Count; j++)
+ {
+ if (i == j) continue;
+
+ var key = Tuple.Create(ProcName, candidates[i].Item1, candidates[j].Item1,
+ candidates[i].Item2, candidates[j].Item2);
+ if (PredicateAbs.unsatPrePredPairs.Contains(key))
+ {
+ isFalse = true;
+ posPreds = new HashSet<int>();
+ negPreds = new HashSet<int>();
+ return;
+ }
+
+ key = Tuple.Create(ProcName, candidates[i].Item1, candidates[j].Item1,
+ candidates[i].Item2, !candidates[j].Item2);
+
+ if (PredicateAbs.unsatPrePredPairs.Contains(key))
+ drop.Add(candidates[j].Item1);
+ }
+ }
+
+ posPreds.ExceptWith(drop);
+ negPreds.ExceptWith(drop);
+ }
+
+ public PredicateAbsConjunct(bool isFalse, string ProcName)
+ {
+ posPreds = new HashSet<int>();
+ negPreds = new HashSet<int>();
+ this.isFalse = isFalse;
+ this.ProcName = ProcName;
+ }
+
+ // do we know that c1 is surely less than or equal to c2? That is, c1 describes a smaller
+ // concretization. We check that c2 is a sub-conjunct of c1
+ public static bool syntacticLessThan(PredicateAbsConjunct c1, PredicateAbsConjunct c2)
+ {
+ if (c1.isFalse) return true;
+ if (c2.isFalse) return false;
+ return (c2.posPreds.IsSubsetOf(c1.posPreds) && c2.negPreds.IsSubsetOf(c1.negPreds));
+ }
+
+ public static PredicateAbsConjunct Singleton(int v, bool isPositive, string ProcName)
+ {
+ if (isPositive)
+ return new PredicateAbsConjunct(new int[] { v }, new HashSet<int>(), ProcName);
+ else
+ return new PredicateAbsConjunct(new HashSet<int>(), new int[] { v }, ProcName);
+ }
+
+ public PredicateAbsConjunct(IEnumerable<int> pos, IEnumerable<int> neg, string ProcName)
+ {
+ isFalse = false;
+ posPreds = new HashSet<int>(pos);
+ negPreds = new HashSet<int>(neg);
+ this.ProcName = ProcName;
+ Normalize();
+ }
+
+ public static PredicateAbsConjunct And(PredicateAbsConjunct v1, PredicateAbsConjunct v2)
+ {
+ if (v1.isFalse || v2.isFalse) return new PredicateAbsConjunct(true, v1.ProcName);
+ var ret = new PredicateAbsConjunct(v1.posPreds.Union(v2.posPreds), v1.negPreds.Union(v2.negPreds), v1.ProcName);
+ ret.StrongNormalize();
+ return ret;
+ }
+
+ public bool implies(PredicateAbsConjunct v)
+ {
+ if (isFalse) return true;
+ if (v.isFalse) return false;
+ return (posPreds.IsSupersetOf(v.posPreds) && negPreds.IsSupersetOf(v.negPreds));
+ }
+
+ public VCExpr ToVcExpr(Func<int, VCExpr> predToExpr, VCExpressionGenerator gen)
+ {
+ if (isFalse) return VCExpressionGenerator.False;
+ var ret = VCExpressionGenerator.True;
+ posPreds.Iter(p => ret = gen.AndSimp(ret, predToExpr(p)));
+ negPreds.Iter(p => ret = gen.AndSimp(ret, gen.Not(predToExpr(p))));
+ return ret;
+ }
+
+ public Expr ToExpr(Func<int, Expr> predToExpr)
+ {
+ if (isFalse) return Expr.False;
+ Expr ret = Expr.True;
+ var pp = posPreds.ToList(); pp.Sort();
+ var np = negPreds.ToList(); np.Sort();
+ pp.Iter(p => ret = Expr.And(ret, predToExpr(p)));
+ np.Iter(p => ret = Expr.And(ret, Expr.Not(predToExpr(p))));
+ return ret;
+ }
+
+ public override string ToString()
+ {
+ if (isFalse)
+ return "false";
+
+ var ret = "";
+ var first = true;
+ foreach (var p in posPreds)
+ {
+ ret += string.Format("{0}{1}", first ? "" : " && ", PredicateAbs.PrePreds[ProcName][p]);
+ first = false;
+ }
+ foreach (var p in negPreds)
+ {
+ ret += string.Format("{0}!{1}", first ? "" : " && ", PredicateAbs.PrePreds[ProcName][p]);
+ first = false;
+ }
+ return ret;
+ }
+ }
+
+ class FindSummaryPred : MutatingVCExprVisitor<bool>
+ {
+ public List<Tuple<string, bool, VCExprVar, VCExprNAry>> summaryPreds;
+ int assertId;
+ private static int CounterId = 0;
+
+ public FindSummaryPred(VCExpressionGenerator gen, int assertId)
+ : base(gen)
+ {
+ summaryPreds = new List<Tuple<string, bool, VCExprVar, VCExprNAry>>();
+ this.assertId = assertId;
+ }
+
+ protected override VCExpr/*!*/ UpdateModifiedNode(VCExprNAry/*!*/ originalNode,
+ List<VCExpr/*!*/>/*!*/ newSubExprs,
+ // has any of the subexpressions changed?
+ bool changed,
+ bool arg)
+ {
+ Contract.Ensures(Contract.Result<VCExpr>() != null);
+
+ VCExpr ret;
+ if (changed)
+ ret = Gen.Function(originalNode.Op,
+ newSubExprs, originalNode.TypeArguments);
+ else
+ ret = originalNode;
+
+ VCExprNAry retnary = ret as VCExprNAry;
+ if (retnary == null) return ret;
+ var op = retnary.Op as VCExprBoogieFunctionOp;
+
+ if (op == null)
+ {
+ var lop = retnary.Op as VCExprLabelOp;
+ if (lop == null) return ret;
+ if (lop.pos) return ret;
+ if (!lop.label.Equals("@" + assertId.ToString())) return ret;
+
+ //var subexpr = retnary[0] as VCExprNAry;
+ //if (subexpr == null) return ret;
+ //op = subexpr.Op as VCExprBoogieFunctionOp;
+ //if (op == null) return ret;
+
+ var subexpr = retnary[0] as VCExprVar;
+ if (subexpr == null) return ret;
+ if (!subexpr.Name.StartsWith("AbstractHoudiniControl")) return ret;
+
+ for (int i = 0; i < summaryPreds.Count; i++)
+ {
+ if (summaryPreds[i].Item3 == subexpr)
+ summaryPreds[i] = Tuple.Create(summaryPreds[i].Item1, true, summaryPreds[i].Item3, summaryPreds[i].Item4);
+ }
+ return ret;
+ }
+
+ string calleeName = op.Func.Name;
+
+ if (!calleeName.EndsWith(AbstractHoudini.summaryPredSuffix))
+ return ret;
+
+ var controlConst = Gen.Variable("AbstractHoudiniControl" + CounterId, Microsoft.Boogie.Type.Bool);
+ CounterId++;
+
+ summaryPreds.Add(Tuple.Create(calleeName.Substring(0, calleeName.Length - AbstractHoudini.summaryPredSuffix.Length), false, controlConst, retnary));
+
+ return controlConst;
+ //return ret;
+ }
+
+ }
+
+ class FindExistentialFunctions : MutatingVCExprVisitor<bool>
+ {
+ public List<Tuple<string, VCExprVar, VCExprNAry>> funcCalls;
+ private HashSet<string> existentialFunctions;
+ private static int CounterId = 0;
+
+ public FindExistentialFunctions(VCExpressionGenerator gen, HashSet<string> existentialFunctions)
+ : base(gen)
+ {
+ funcCalls = new List<Tuple<string, VCExprVar, VCExprNAry>>();
+ this.existentialFunctions = existentialFunctions;
+ }
+
+ protected override VCExpr/*!*/ UpdateModifiedNode(VCExprNAry/*!*/ originalNode,
+ List<VCExpr/*!*/>/*!*/ newSubExprs,
+ // has any of the subexpressions changed?
+ bool changed,
+ bool arg)
+ {
+ Contract.Ensures(Contract.Result<VCExpr>() != null);
+
+ VCExpr ret;
+ if (changed)
+ ret = Gen.Function(originalNode.Op,
+ newSubExprs, originalNode.TypeArguments);
+ else
+ ret = originalNode;
+
+ VCExprNAry retnary = ret as VCExprNAry;
+ if (retnary == null) return ret;
+ var op = retnary.Op as VCExprBoogieFunctionOp;
+ if (op == null) return ret;
+
+ string calleeName = op.Func.Name;
+
+ if (!existentialFunctions.Contains(calleeName))
+ return ret;
+
+ var controlConst = Gen.Variable("AbsHoudiniControl" + CounterId, Microsoft.Boogie.Type.Bool);
+ CounterId++;
+
+ funcCalls.Add(Tuple.Create(calleeName, controlConst, retnary));
+
+ return controlConst;
+ }
+
+ }
+
+ class AbstractHoudiniErrorReporter : ProverInterface.ErrorHandler
+ {
+ public Model model;
+
+ public AbstractHoudiniErrorReporter()
+ {
+ model = null;
+ }
+
+ public override void OnModel(IList<string> labels, Model model, ProverInterface.Outcome proverOutcome)
+ {
+ Debug.Assert(model != null);
+ if(CommandLineOptions.Clo.PrintErrorModel >= 1) model.Write(Console.Out);
+ this.model = model;
+ }
+ }
+
+ public class AbsHoudiniInternalError : System.ApplicationException
+ {
+ public AbsHoudiniInternalError(string msg) : base(msg) { }
+
+ };
+
+ public class SimpleUtil
+ {
+ // Constructs a mapping from procedure names to the implementation
+ public static Dictionary<string, Implementation> nameImplMapping(Program p)
+ {
+ var m = new Dictionary<string, Implementation>();
+ foreach (var impl in p.Implementations)
+ {
+ m.Add(impl.Name, impl);
+ }
+
+ return m;
+ }
+
+ // is "assert true"?
+ public static bool isAssertTrue(Cmd cmd)
+ {
+ var acmd = cmd as AssertCmd;
+ if (acmd == null) return false;
+ var le = acmd.Expr as LiteralExpr;
+ if (le == null) return false;
+ if (le.IsTrue) return true;
+ return false;
+ }
+ }
+
+}
diff --git a/Source/Houdini/AnnotationDependenceAnalyser.cs b/Source/Houdini/AnnotationDependenceAnalyser.cs
index e925d413..dacac233 100644
--- a/Source/Houdini/AnnotationDependenceAnalyser.cs
+++ b/Source/Houdini/AnnotationDependenceAnalyser.cs
@@ -1,878 +1,878 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Microsoft.Boogie.GraphUtil;
-using Microsoft.Basetypes;
-using System.Diagnostics;
-
-namespace Microsoft.Boogie.Houdini {
-
- public class AnnotationDependenceAnalyser {
-
- private const string COARSE_STAGES = "COARSE";
- private const string FINE_STAGES = "FINE";
- private const string BALANCED_STAGES = "BALANCED";
-
- private Program prog;
- private IVariableDependenceAnalyser varDepAnalyser;
- private IEnumerable<string> CandidateIdentifiers; // Candidate Boolean names
- private IEnumerable<string> NonCandidateIdentifiers; // Additional names introduced for non-candidate annotations
- private Dictionary<string, HashSet<VariableDescriptor>> annotationDependsOn;
- private Dictionary<VariableDescriptor, HashSet<string>> variableDirectlyReferredToByAnnotations;
- private Graph<string> AnnotationDependences;
- private StronglyConnectedComponents<string> SCCs;
- private Graph<SCC<string>> StagesDAG;
- private StagedHoudiniPlan Plan;
-
- public AnnotationDependenceAnalyser(Program prog) {
- this.prog = prog;
- this.varDepAnalyser = new VariableDependenceAnalyser(prog);
- varDepAnalyser.Analyse();
- }
-
- public void Analyse() {
- if (CommandLineOptions.Clo.Trace) {
- Console.WriteLine("Annotation dependence analysis: Getting annotations");
- }
-
- CandidateIdentifiers = GetCandidates();
- NonCandidateIdentifiers = GetNonCandidateAnnotations();
-
- DetermineAnnotationVariableDependences();
-
- ConstructAnnotationDependenceGraph();
-
- ConstructStagesDAG();
-
- }
-
- private IEnumerable<string> AllAnnotationIdentifiers() {
- HashSet<string> Result = new HashSet<string>();
- foreach (var c in CandidateIdentifiers) {
- Result.Add(c);
- }
- foreach (var a in NonCandidateIdentifiers) {
- Result.Add(a);
- }
- return Result;
- }
-
- private void ConstructStagesDAG()
- {
- if (CommandLineOptions.Clo.Trace)
- {
- Console.WriteLine("Annotation dependence analysis: Computing SCCs");
- }
-
- Adjacency<string> next = new Adjacency<string>(AnnotationDependences.Successors);
- Adjacency<string> prev = new Adjacency<string>(AnnotationDependences.Predecessors);
- SCCs = new StronglyConnectedComponents<string>(
- AnnotationDependences.Nodes, next, prev);
- SCCs.Compute();
-
- if (CommandLineOptions.Clo.Trace)
- {
- Console.WriteLine("Annotation dependence analysis: Building stages DAG");
- }
-
- Dictionary<string, SCC<string>> rep = new Dictionary<string, SCC<string>>();
- foreach (var scc in SCCs)
- {
- foreach (var s in scc)
- {
- rep[s] = scc;
- }
- }
-
- StagesDAG = new Graph<SCC<string>>();
-
- foreach (var edge in AnnotationDependences.Edges)
- {
- if (rep[edge.Item1] != rep[edge.Item2])
- {
- StagesDAG.AddEdge(rep[edge.Item1], rep[edge.Item2]);
- }
- }
-
- SCC<string> dummy = new SCC<string>();
- foreach (var scc in SCCs)
- {
- StagesDAG.AddEdge(scc, dummy);
- }
- }
-
- private void ConstructAnnotationDependenceGraph()
- {
- if (CommandLineOptions.Clo.Trace)
- {
- Console.WriteLine("Annotation dependence analysis: Building dependence graph");
- }
-
- IAnnotationReachabilityChecker reachabilityChecker;
-
- if(CommandLineOptions.Clo.StagedHoudiniReachabilityAnalysis) {
- reachabilityChecker = new AnnotationReachabilityChecker(prog, AllAnnotationIdentifiers());
- } else {
- reachabilityChecker = new DummyAnnotationReachabilityChecker();
- }
-
- AnnotationDependences = new Graph<string>();
- foreach (var c in AllAnnotationIdentifiers())
- {
- AnnotationDependences.AddEdge(c, c);
- foreach (var vd in annotationDependsOn[c])
- {
- if (variableDirectlyReferredToByAnnotations.ContainsKey(vd))
- {
- foreach (var d in variableDirectlyReferredToByAnnotations[vd])
- {
- if(reachabilityChecker.MayReach(d, c))
- {
- AnnotationDependences.AddEdge(c, d);
- }
- }
- }
- }
- }
-
- if(CommandLineOptions.Clo.StagedHoudiniMergeIgnoredAnnotations) {
- MergeIgnoredAnnotations();
- }
-
- }
-
- private void MergeIgnoredAnnotations()
- {
- var IgnoredAnnotationsToVariables = new Dictionary<string, HashSet<Variable>>();
- foreach(var c in AllAnnotationIdentifiers()) {
- IgnoredAnnotationsToVariables[c] = new HashSet<Variable>();
- }
- foreach(var ci in AnnotationInstances()) {
- if(!IgnoredAnnotationsToVariables.ContainsKey(ci.AnnotationIdentifier)) {
- continue;
- }
- VariableCollector vc = new VariableCollector();
- vc.Visit(ci.Expr);
- if(vc.usedVars.Select(Item => varDepAnalyser.VariableRelevantToAnalysis(Item, ci.Proc)).Count() != 0) {
- continue;
- }
- foreach(var v in vc.usedVars) {
- if(varDepAnalyser.Ignoring(v, ci.Proc)) {
- IgnoredAnnotationsToVariables[ci.AnnotationIdentifier].Add(v);
- }
- }
- }
- foreach(var c in IgnoredAnnotationsToVariables.Keys) {
- foreach(var d in IgnoredAnnotationsToVariables.Keys) {
- if(c == d) {
- continue;
- }
- if(IgnoredAnnotationsToVariables[c].Equals(IgnoredAnnotationsToVariables[d])) {
- AnnotationDependences.AddEdge(c, d);
- }
- }
- }
- }
-
-
-
- private void DetermineAnnotationVariableDependences()
- {
- if (CommandLineOptions.Clo.Trace)
- {
- Console.WriteLine("Annotation dependence analysis: Working out what annotations depend on");
- }
- annotationDependsOn = new Dictionary<string, HashSet<VariableDescriptor>>();
- variableDirectlyReferredToByAnnotations = new Dictionary<VariableDescriptor, HashSet<string>>();
- foreach (var c in AllAnnotationIdentifiers())
- {
- annotationDependsOn[c] = new HashSet<VariableDescriptor>();
- }
-
- foreach(var annotationInstance in AnnotationInstances()) {
- AddDependences(annotationInstance);
- }
-
- }
-
- private IEnumerable<AnnotationInstance> AnnotationInstances()
- {
- foreach (var impl in prog.Implementations)
- {
- foreach (PredicateCmd p in impl.Blocks.SelectMany(Item => Item.Cmds).OfType<PredicateCmd>())
- {
- string c;
- if(Houdini.MatchCandidate(p.Expr, CandidateIdentifiers, out c)) {
- yield return new AnnotationInstance(c, impl.Name, p.Expr);
- } else if((p is AssertCmd) && QKeyValue.FindBoolAttribute(p.Attributes, "originated_from_invariant")) {
- var tag = GetTagFromNonCandidateAttributes(p.Attributes);
- if (tag != null) {
- yield return new AnnotationInstance(tag, impl.Name, p.Expr);
- }
- }
- }
- }
-
- foreach (var proc in prog.NonInlinedProcedures())
- {
- foreach (Requires r in proc.Requires)
- {
- string c;
- if(Houdini.MatchCandidate(r.Condition, CandidateIdentifiers, out c)) {
- yield return new AnnotationInstance(c, proc.Name, r.Condition);
- } else {
- var tag = GetTagFromNonCandidateAttributes(r.Attributes);
- if (tag != null) {
- yield return new AnnotationInstance(tag, proc.Name, r.Condition);
- }
- }
- }
- foreach (Ensures e in proc.Ensures)
- {
- string c;
- if(Houdini.MatchCandidate(e.Condition, CandidateIdentifiers, out c)) {
- yield return new AnnotationInstance(c, proc.Name, e.Condition);
- } else {
- var tag = GetTagFromNonCandidateAttributes(e.Attributes);
- if (tag != null) {
- yield return new AnnotationInstance(tag, proc.Name, e.Condition);
- }
- }
- }
- }
- }
-
- internal static string GetTagFromNonCandidateAttributes(QKeyValue Attributes)
- {
- string tag = QKeyValue.FindStringAttribute(Attributes, "staged_houdini_tag");
- return tag;
- }
-
- private bool FindInDAG(Graph<SCC<string>> DAG, SCC<string> toFind, SCC<string> start) {
- if (toFind == start) {
- return true;
- }
- foreach (var n in DAG.Successors(start)) {
- if (FindInDAG(DAG, toFind, n)) {
- return true;
- }
- }
- return false;
- }
-
- private void AddDependences(AnnotationInstance ci) {
- VariableCollector vc = new VariableCollector();
- vc.VisitExpr(ci.Expr);
-
- foreach (var v in vc.usedVars.Where(Item => varDepAnalyser.VariableRelevantToAnalysis(Item, ci.Proc))) {
- VariableDescriptor vd =
- varDepAnalyser.MakeDescriptor(ci.Proc, v);
- if (!variableDirectlyReferredToByAnnotations.ContainsKey(vd)) {
- variableDirectlyReferredToByAnnotations[vd] = new HashSet<string>();
- }
- variableDirectlyReferredToByAnnotations[vd].Add(ci.AnnotationIdentifier);
-
- foreach (var w in varDepAnalyser.DependsOn(vd)) {
- annotationDependsOn[ci.AnnotationIdentifier].Add(w);
- }
- }
- }
-
- private bool IsStageDependence(SCC<string> Src, SCC<string> Dst) {
- foreach (var c in Src) {
- foreach (var d in AnnotationDependences.Successors(c)) {
- if (Dst.Contains(d)) {
- return true;
- }
- }
- }
- return false;
- }
-
-
- public void dump() {
-
- if(CommandLineOptions.Clo.DebugStagedHoudini) {
- varDepAnalyser.dump();
-
- Console.WriteLine("Annotations and the variables they depend on");
- Console.WriteLine("============================================");
- foreach (var entry in annotationDependsOn) {
- Console.WriteLine(entry.Key + " <- ");
- foreach (var vd in entry.Value) {
- Console.WriteLine(" " + vd + ", ");
- }
- }
-
- Console.WriteLine("");
-
- Console.WriteLine("Variables and the annotations that directly refer to them");
- Console.WriteLine("========================================================");
- foreach (var entry in variableDirectlyReferredToByAnnotations) {
- Console.WriteLine(entry.Key + " <- ");
- foreach (var annotation in entry.Value) {
- Console.WriteLine(" " + annotation + ", ");
- }
- }
-
- Console.WriteLine("");
-
- Console.WriteLine("Annotation dependence graph");
- Console.WriteLine("==========================");
- foreach (var c in AnnotationDependences.Nodes) {
- Console.WriteLine(c + " <- ");
- foreach (var d in AnnotationDependences.Successors(c)) {
- Console.WriteLine(" " + d);
- }
- }
- }
-
- Console.WriteLine("");
-
- Console.WriteLine("Strongly connected components");
- Console.WriteLine("=============================");
-
- List<SCC<string>> Components = StagesDAG.TopologicalSort().ToList();
-
- for (int i = 0; i < Components.Count(); i++) {
- Console.Write(i + ": ");
- DumpSCC(Components[i]);
- Console.WriteLine(); Console.WriteLine();
- }
-
- Console.WriteLine("Stages DAG");
- Console.WriteLine("==========");
- for (int i = 0; i < Components.Count(); i++) {
- Console.Write(i + " -> { ");
- bool first = true;
- foreach (var d in StagesDAG.Successors(Components[i])) {
- if (first) {
- first = false;
- }
- else {
- Console.Write(", ");
- }
- Console.Write(Components.IndexOf(d));
- }
- Console.WriteLine(" }");
- }
-
- }
-
- private static void DumpSCC(SCC<string> component) {
- var sortedComponent = component.ToList();
- sortedComponent.Sort();
- Console.Write("{ ");
- bool first = true;
- foreach (var s in sortedComponent) {
- if (first) {
- first = false;
- }
- else {
- Console.Write(", ");
- }
- Console.Write(s);
- }
- Console.Write(" }");
- }
-
- private bool OnlyRefersToConstants(Expr e) {
- VariableCollector vc = new VariableCollector();
- vc.Visit(e);
- return vc.usedVars.OfType<Constant>().Count() == vc.usedVars.Count();
- }
-
- private IEnumerable<string> GetNonCandidateAnnotations() {
- var Result = new HashSet<string>();
- int Counter = 0;
- foreach(var Assertion in prog.Blocks().SelectMany(Item => Item.Cmds).
- OfType<AssertCmd>()) {
-
- string unused;
- if (Houdini.MatchCandidate(Assertion.Expr, CandidateIdentifiers, out unused)) {
- continue;
- }
-
- if (!QKeyValue.FindBoolAttribute(Assertion.Attributes, "originated_from_invariant")) {
- continue;
- }
-
- if (OnlyRefersToConstants(Assertion.Expr)) {
- continue;
- }
-
- string Tag = "staged_houdini_tag_" + Counter;
- Result.Add(Tag);
- Assertion.Attributes = new QKeyValue(Token.NoToken, "staged_houdini_tag",
- new List<object> { Tag }, Assertion.Attributes);
- Counter++;
- }
-
- foreach(var Req in prog.NonInlinedProcedures().SelectMany(Item => Item.Requires)) {
-
- string unused;
- if (Houdini.MatchCandidate(Req.Condition, CandidateIdentifiers, out unused)) {
- continue;
- }
-
- if (OnlyRefersToConstants(Req.Condition)) {
- continue;
- }
-
- string Tag = "staged_houdini_tag_" + Counter;
- Result.Add(Tag);
- Req.Attributes = new QKeyValue(Token.NoToken, "staged_houdini_tag",
- new List<object> { Tag }, Req.Attributes);
- Counter++;
-
- }
-
- foreach(var Ens in prog.NonInlinedProcedures().SelectMany(Item => Item.Ensures)) {
-
- string unused;
- if (Houdini.MatchCandidate(Ens.Condition, CandidateIdentifiers, out unused)) {
- continue;
- }
-
- if (OnlyRefersToConstants(Ens.Condition)) {
- continue;
- }
-
- string Tag = "staged_houdini_tag_" + Counter;
- Result.Add(Tag);
- Ens.Attributes = new QKeyValue(Token.NoToken, "staged_houdini_tag",
- new List<object> { Tag }, Ens.Attributes);
- Counter++;
-
- }
-
- return Result;
- }
-
- private IEnumerable<string> GetCandidates() {
- return prog.Variables.Where(Item =>
- QKeyValue.FindBoolAttribute(Item.Attributes, "existential")).Select(Item => Item.Name);
- }
-
-
- public StagedHoudiniPlan ApplyStages() {
-
- if (NoStages())
- {
- Debug.Assert(false);
- var TrivialGraph = new Graph<ScheduledStage>();
- TrivialGraph.AddSource(new ScheduledStage(0, new HashSet<string>()));
- return new StagedHoudiniPlan(TrivialGraph);
- }
-
- #region Assign annotations to stages at a given level of granularity
-
- switch(CommandLineOptions.Clo.StagedHoudini) {
- case COARSE_STAGES:
- Plan = ComputeCoarseStages();
- break;
- case FINE_STAGES:
- Plan = ComputeFineStages();
- break;
- case BALANCED_STAGES:
- Plan = ComputeBalancedStages();
- break;
- default:
- Debug.Assert(false);
- Plan = null;
- break;
- }
-
- foreach(var c in AllAnnotationIdentifiers()) {
- Debug.Assert(Plan.StageForAnnotation(c) != null);
- }
- #endregion
-
- #region Generate boolean variables to control stages
- var stageToActiveBoolean = new Dictionary<int, Constant>();
- var stageToCompleteBoolean = new Dictionary<int, Constant>();
-
- foreach (var stage in Plan)
- {
- var stageActive = new Constant(Token.NoToken,
- new TypedIdent(Token.NoToken, "_stage_" + stage.GetId() + "_active", Type.Bool),
- false);
- stageActive.AddAttribute("stage_active", new object[] { new LiteralExpr(Token.NoToken, BigNum.FromInt(stage.GetId())) });
- prog.AddTopLevelDeclaration(stageActive);
- stageToActiveBoolean[stage.GetId()] = stageActive;
-
- var stageComplete = new Constant(Token.NoToken,
- new TypedIdent(Token.NoToken, "_stage_" + stage.GetId() + "_complete", Type.Bool),
- false);
- stageComplete.AddAttribute("stage_complete", new object[] { new LiteralExpr(Token.NoToken, BigNum.FromInt(stage.GetId())) });
- prog.AddTopLevelDeclaration(stageComplete);
- stageToCompleteBoolean[stage.GetId()] = stageComplete;
- }
- #endregion
-
- #region Adapt annotation assertions to take account of stages
- foreach (var b in prog.Implementations.Select(Item => Item.Blocks).SelectMany(Item => Item))
- {
- List<Cmd> newCmds = new List<Cmd>();
- foreach (var cmd in b.Cmds)
- {
- var a = cmd as AssertCmd;
- string c;
- if (a != null) {
- if (Houdini.MatchCandidate(a.Expr, CandidateIdentifiers, out c))
- {
- newCmds.Add(new AssertCmd(a.tok, Houdini.AddConditionToCandidate(a.Expr,
- Expr.Ident(stageToActiveBoolean[Plan.StageForAnnotation(c).GetId()]), c), a.Attributes));
- newCmds.Add(new AssumeCmd(a.tok, Houdini.AddConditionToCandidate(a.Expr,
- Expr.Ident(stageToCompleteBoolean[Plan.StageForAnnotation(c).GetId()]), c), a.Attributes));
- } else if (QKeyValue.FindBoolAttribute(a.Attributes, "originated_from_invariant")) {
- string tag = GetTagFromNonCandidateAttributes(a.Attributes);
- if (tag == null) {
- newCmds.Add(a);
- } else {
- newCmds.Add(new AssertCmd(a.tok, Expr.Imp(
- Expr.Ident(stageToActiveBoolean[Plan.StageForAnnotation(tag).GetId()]), a.Expr),
- a.Attributes));
- newCmds.Add(new AssumeCmd(a.tok, Expr.Imp(
- Expr.Ident(stageToCompleteBoolean[Plan.StageForAnnotation(tag).GetId()]), a.Expr),
- a.Attributes));
- }
- }
- }
- else
- {
- newCmds.Add(cmd);
- }
- }
- b.Cmds = newCmds;
- }
- #endregion
-
- #region Adapt pre/postconditions to take account of stages
- foreach (var p in prog.NonInlinedProcedures())
- {
-
- #region Handle the preconditions
- {
- List<Requires> newRequires = new List<Requires>();
- foreach(Requires r in p.Requires) {
- string c;
- if (Houdini.MatchCandidate(r.Condition, CandidateIdentifiers, out c)) {
- newRequires.Add(new Requires(r.tok, false,
- Houdini.AddConditionToCandidate(r.Condition,
- Expr.Ident(stageToActiveBoolean[Plan.StageForAnnotation(c).GetId()]), c),
- r.Comment, r.Attributes));
- newRequires.Add(new Requires(r.tok, true,
- Houdini.AddConditionToCandidate(r.Condition,
- Expr.Ident(stageToCompleteBoolean[Plan.StageForAnnotation(c).GetId()]), c),
- r.Comment, r.Attributes));
- } else {
- string tag = GetTagFromNonCandidateAttributes(r.Attributes);
- if (tag == null) {
- newRequires.Add(r);
- } else {
- newRequires.Add(new Requires(r.tok, false,
- Expr.Imp(Expr.Ident(stageToActiveBoolean[Plan.StageForAnnotation(tag).GetId()]), r.Condition),
- r.Comment, r.Attributes));
- newRequires.Add(new Requires(r.tok, true,
- Expr.Imp(Expr.Ident(stageToCompleteBoolean[Plan.StageForAnnotation(tag).GetId()]), r.Condition),
- r.Comment, r.Attributes));
- }
- }
- }
- p.Requires = newRequires;
- }
- #endregion
-
- #region Handle the postconditions
- {
- List<Ensures> newEnsures = new List<Ensures>();
- foreach(Ensures e in p.Ensures) {
- string c;
- if (Houdini.MatchCandidate(e.Condition, CandidateIdentifiers, out c)) {
- int stage = Plan.StageForAnnotation(c).GetId();
- newEnsures.Add(new Ensures(e.tok, false,
- Houdini.AddConditionToCandidate(e.Condition,
- Expr.Ident(stageToActiveBoolean[stage]), c),
- e.Comment, e.Attributes));
- newEnsures.Add(new Ensures(e.tok, true,
- Houdini.AddConditionToCandidate(e.Condition,
- Expr.Ident(stageToCompleteBoolean[stage]), c),
- e.Comment, e.Attributes));
- } else {
- string tag = GetTagFromNonCandidateAttributes(e.Attributes);
- if (tag == null) {
- newEnsures.Add(e);
- } else {
- newEnsures.Add(new Ensures(e.tok, false,
- Expr.Imp(Expr.Ident(stageToActiveBoolean[Plan.StageForAnnotation(tag).GetId()]), e.Condition),
- e.Comment, e.Attributes));
- newEnsures.Add(new Ensures(e.tok, true,
- Expr.Imp(Expr.Ident(stageToCompleteBoolean[Plan.StageForAnnotation(tag).GetId()]), e.Condition),
- e.Comment, e.Attributes));
- }
- }
- }
- p.Ensures = newEnsures;
- }
- #endregion
-
- }
- #endregion
-
- return Plan;
-
- }
-
- private int FindLargestStage() {
- return StagesDAG.Nodes.Select(Item => Item.Count()).Max();
- }
-
-
- private StagedHoudiniPlan ComputeCoarseStages()
- {
- foreach(var n in StagesDAG.Nodes) {
- Debug.Assert(!StagesDAG.Successors(n).Contains(n));
- }
-
- Graph<ScheduledStage> Dependences = new Graph<ScheduledStage>();
-
- var done = new Dictionary<SCC<string>, ScheduledStage>();
- done[GetStartNodeOfStagesDAG()] = new ScheduledStage(0, new HashSet<string>());
-
- for(int stageId = 1; done.Count() != StagesDAG.Nodes.Count(); stageId++)
- {
- var Stage = new ScheduledStage(stageId, new HashSet<string>());
- HashSet<SCC<string>> AssignedToThisStage = new HashSet<SCC<string>>();
-
- foreach (var n in StagesDAG.Nodes.Where(Item => !done.ContainsKey(Item)))
- {
- if(StagesDAG.Successors(n).Where(Item => !done.ContainsKey(Item)).Count() == 0) {
- foreach(var s in StagesDAG.Successors(n)) {
- Debug.Assert(s != n);
- Debug.Assert(Stage != done[s]);
- Dependences.AddEdge(Stage, done[s]);
- }
- foreach (var a in n)
- {
- Stage.AddAnnotation(a);
- }
- AssignedToThisStage.Add(n);
- }
- }
-
- foreach(var n in AssignedToThisStage) {
- done[n] = Stage;
- }
- }
- return new StagedHoudiniPlan(Dependences);
- }
-
- private StagedHoudiniPlan ComputeBalancedStages()
- {
- Graph<ScheduledStage> Dependences = new Graph<ScheduledStage>();
- var done = new Dictionary<SCC<string>, ScheduledStage>();
- done[GetStartNodeOfStagesDAG()] = new ScheduledStage(0, new HashSet<string>());
-
- int maxStageSize = 200;
-
- for(int stageId = 1; done.Count() != StagesDAG.Nodes.Count(); stageId++)
- {
- int stageSize = 0;
- ScheduledStage Stage = new ScheduledStage(stageId, new HashSet<string>());
- HashSet<SCC<string>> AddedToThisStage = new HashSet<SCC<string>>();
-
- foreach (var n in StagesDAG.Nodes.Where(Item => !done.ContainsKey(Item)))
- {
- if(stageSize + n.Count() > maxStageSize) {
- continue;
- }
- if(StagesDAG.Successors(n).Where(Item => !done.ContainsKey(Item)).Count() == 0) {
- foreach (var c in n)
- {
- Stage.AddAnnotation(c);
- stageSize++;
- }
- foreach(var s in StagesDAG.Successors(n)) {
- Dependences.AddEdge(Stage, done[s]);
- }
- AddedToThisStage.Add(n);
- }
- }
- foreach(var n in AddedToThisStage) {
- done[n] = Stage;
- }
- if(stageSize == 0) {
- maxStageSize *= 2;
- }
- }
- return new StagedHoudiniPlan(Dependences);
- }
-
- private StagedHoudiniPlan ComputeFineStages()
- {
- Graph<ScheduledStage> Dependences = new Graph<ScheduledStage>();
- var done = new Dictionary<SCC<string>, ScheduledStage>();
-
- List<SCC<string>> components = StagesDAG.TopologicalSort().ToList();
- components.Reverse();
-
- for (int i = 0; i < components.Count(); i++)
- {
- ScheduledStage Stage = new ScheduledStage(i, new HashSet<string>());
- done[components[i]] = Stage;
- foreach (var c in components[i])
- {
- Stage.AddAnnotation(c);
- }
- foreach(var s in StagesDAG.Successors(components[i])) {
- Dependences.AddEdge(Stage, done[s]);
- }
- }
- return new StagedHoudiniPlan(Dependences);
- }
-
- private SCC<string> GetStartNodeOfStagesDAG()
- {
- return StagesDAG.Nodes.Where(Item => StagesDAG.Successors(Item).Count() == 0).
- ToList()[0];
- }
-
- private bool NoStages()
- {
- return AllAnnotationIdentifiers().Count() == 0 || StagesDAG.Nodes.Count() == 0;
- }
- }
-
- interface IAnnotationReachabilityChecker {
- bool MayReach(string c, string d);
- }
-
- class DummyAnnotationReachabilityChecker : IAnnotationReachabilityChecker {
- public bool MayReach(string c, string d) {
- return true;
- }
- }
-
- class AnnotationReachabilityChecker : IAnnotationReachabilityChecker {
-
- private enum PrePost {
- PRE, POST
- }
-
- private Program prog;
- private IEnumerable<string> AnnotationIdentifiers;
- private IInterproceduralReachabilityGraph reachabilityGraph;
- private Dictionary<string, HashSet<object>> annotationToOccurences;
-
- internal AnnotationReachabilityChecker(Program prog, IEnumerable<string> AnnotationIdentifiers) {
- this.prog = prog;
- this.AnnotationIdentifiers = AnnotationIdentifiers;
- this.reachabilityGraph = new InterproceduralReachabilityGraph(prog);
- this.annotationToOccurences = new Dictionary<string,HashSet<object>>();
-
- // Add all annotation occurrences in blocks
- foreach(Block b in prog.Blocks()) {
- foreach(var assertCmd in b.Cmds.OfType<AssertCmd>()) {
- string c;
- if(Houdini.MatchCandidate(assertCmd.Expr, AnnotationIdentifiers, out c)) {
- AddAnnotationOccurrence(c, b);
- } else {
- var tag = AnnotationDependenceAnalyser.GetTagFromNonCandidateAttributes(assertCmd.Attributes);
- if (tag != null) {
- AddAnnotationOccurrence(tag, b);
- }
- }
- }
- }
-
- // Add all annotation occurrences in pre and post conditions
- foreach(var proc in prog.NonInlinedProcedures()) {
- foreach(Requires r in proc.Requires) {
- string c;
- if(Houdini.MatchCandidate(r.Condition, AnnotationIdentifiers, out c)) {
- AddAnnotationOccurrence(c, new Tuple<string, PrePost>(proc.Name, PrePost.PRE));
- } else {
- string tag = AnnotationDependenceAnalyser.GetTagFromNonCandidateAttributes(r.Attributes);
- if(tag != null) {
- AddAnnotationOccurrence(tag, new Tuple<string, PrePost>(proc.Name, PrePost.PRE));
- }
- }
- }
- foreach(Ensures e in proc.Ensures) {
- string c;
- if(Houdini.MatchCandidate(e.Condition, AnnotationIdentifiers, out c)) {
- AddAnnotationOccurrence(c, new Tuple<string, PrePost>(proc.Name, PrePost.POST));
- } else {
- string tag = AnnotationDependenceAnalyser.GetTagFromNonCandidateAttributes(e.Attributes);
- if(tag != null) {
- AddAnnotationOccurrence(tag, new Tuple<string, PrePost>(proc.Name, PrePost.PRE));
- }
- }
- }
- }
-
- }
-
- private void AddAnnotationOccurrence(string c, object o) {
- Debug.Assert(o is Block || o is Tuple<string, PrePost>);
- if(!annotationToOccurences.ContainsKey(c)) {
- annotationToOccurences[c] = new HashSet<object>();
- }
- annotationToOccurences[c].Add(o);
- }
-
- public bool MayReach(string c, string d) {
- foreach(object cOccurrence in annotationToOccurences[c]) {
- foreach(object dOccurrence in annotationToOccurences[d]) {
- if(OccurrencesMayReach(cOccurrence, dOccurrence)) {
- return true;
- }
- }
- }
- return false;
- }
-
- private bool OccurrencesMayReach(object cOccurrence, object dOccurrence) {
- Debug.Assert(cOccurrence is Block || cOccurrence is Tuple<string, PrePost>);
- Debug.Assert(dOccurrence is Block || dOccurrence is Tuple<string, PrePost>);
-
- Block cInterproceduralBlock = GetInterproceduralBlock(cOccurrence);
- Block dInterproceduralBlock = GetInterproceduralBlock(dOccurrence);
-
- return reachabilityGraph.MayReach(cInterproceduralBlock, dInterproceduralBlock);
-
- }
-
- private Block GetInterproceduralBlock(object cOccurrence)
- {
- Debug.Assert(cOccurrence is Block || cOccurrence is Tuple<string, PrePost>);
-
- var stringPrePostPair = cOccurrence as Tuple<string, PrePost>;
- if(stringPrePostPair != null) {
- if(stringPrePostPair.Item2 == PrePost.PRE) {
- return reachabilityGraph.GetNewEntryBlock(stringPrePostPair.Item1);
- } else {
- return reachabilityGraph.GetNewExitBlock(stringPrePostPair.Item1);
- }
- }
-
- return reachabilityGraph.GetNewBlock((Block)cOccurrence);
-
- }
- }
-
- class AnnotationInstance {
- public string AnnotationIdentifier;
- public string Proc;
- public Expr Expr;
-
- internal AnnotationInstance(string AnnotationIdentifier, string Proc, Expr Expr) {
- this.AnnotationIdentifier = AnnotationIdentifier;
- this.Proc = Proc;
- this.Expr = Expr;
- }
- }
-
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Boogie.GraphUtil;
+using Microsoft.Basetypes;
+using System.Diagnostics;
+
+namespace Microsoft.Boogie.Houdini {
+
+ public class AnnotationDependenceAnalyser {
+
+ private const string COARSE_STAGES = "COARSE";
+ private const string FINE_STAGES = "FINE";
+ private const string BALANCED_STAGES = "BALANCED";
+
+ private Program prog;
+ private IVariableDependenceAnalyser varDepAnalyser;
+ private IEnumerable<string> CandidateIdentifiers; // Candidate Boolean names
+ private IEnumerable<string> NonCandidateIdentifiers; // Additional names introduced for non-candidate annotations
+ private Dictionary<string, HashSet<VariableDescriptor>> annotationDependsOn;
+ private Dictionary<VariableDescriptor, HashSet<string>> variableDirectlyReferredToByAnnotations;
+ private Graph<string> AnnotationDependences;
+ private StronglyConnectedComponents<string> SCCs;
+ private Graph<SCC<string>> StagesDAG;
+ private StagedHoudiniPlan Plan;
+
+ public AnnotationDependenceAnalyser(Program prog) {
+ this.prog = prog;
+ this.varDepAnalyser = new VariableDependenceAnalyser(prog);
+ varDepAnalyser.Analyse();
+ }
+
+ public void Analyse() {
+ if (CommandLineOptions.Clo.Trace) {
+ Console.WriteLine("Annotation dependence analysis: Getting annotations");
+ }
+
+ CandidateIdentifiers = GetCandidates();
+ NonCandidateIdentifiers = GetNonCandidateAnnotations();
+
+ DetermineAnnotationVariableDependences();
+
+ ConstructAnnotationDependenceGraph();
+
+ ConstructStagesDAG();
+
+ }
+
+ private IEnumerable<string> AllAnnotationIdentifiers() {
+ HashSet<string> Result = new HashSet<string>();
+ foreach (var c in CandidateIdentifiers) {
+ Result.Add(c);
+ }
+ foreach (var a in NonCandidateIdentifiers) {
+ Result.Add(a);
+ }
+ return Result;
+ }
+
+ private void ConstructStagesDAG()
+ {
+ if (CommandLineOptions.Clo.Trace)
+ {
+ Console.WriteLine("Annotation dependence analysis: Computing SCCs");
+ }
+
+ Adjacency<string> next = new Adjacency<string>(AnnotationDependences.Successors);
+ Adjacency<string> prev = new Adjacency<string>(AnnotationDependences.Predecessors);
+ SCCs = new StronglyConnectedComponents<string>(
+ AnnotationDependences.Nodes, next, prev);
+ SCCs.Compute();
+
+ if (CommandLineOptions.Clo.Trace)
+ {
+ Console.WriteLine("Annotation dependence analysis: Building stages DAG");
+ }
+
+ Dictionary<string, SCC<string>> rep = new Dictionary<string, SCC<string>>();
+ foreach (var scc in SCCs)
+ {
+ foreach (var s in scc)
+ {
+ rep[s] = scc;
+ }
+ }
+
+ StagesDAG = new Graph<SCC<string>>();
+
+ foreach (var edge in AnnotationDependences.Edges)
+ {
+ if (rep[edge.Item1] != rep[edge.Item2])
+ {
+ StagesDAG.AddEdge(rep[edge.Item1], rep[edge.Item2]);
+ }
+ }
+
+ SCC<string> dummy = new SCC<string>();
+ foreach (var scc in SCCs)
+ {
+ StagesDAG.AddEdge(scc, dummy);
+ }
+ }
+
+ private void ConstructAnnotationDependenceGraph()
+ {
+ if (CommandLineOptions.Clo.Trace)
+ {
+ Console.WriteLine("Annotation dependence analysis: Building dependence graph");
+ }
+
+ IAnnotationReachabilityChecker reachabilityChecker;
+
+ if(CommandLineOptions.Clo.StagedHoudiniReachabilityAnalysis) {
+ reachabilityChecker = new AnnotationReachabilityChecker(prog, AllAnnotationIdentifiers());
+ } else {
+ reachabilityChecker = new DummyAnnotationReachabilityChecker();
+ }
+
+ AnnotationDependences = new Graph<string>();
+ foreach (var c in AllAnnotationIdentifiers())
+ {
+ AnnotationDependences.AddEdge(c, c);
+ foreach (var vd in annotationDependsOn[c])
+ {
+ if (variableDirectlyReferredToByAnnotations.ContainsKey(vd))
+ {
+ foreach (var d in variableDirectlyReferredToByAnnotations[vd])
+ {
+ if(reachabilityChecker.MayReach(d, c))
+ {
+ AnnotationDependences.AddEdge(c, d);
+ }
+ }
+ }
+ }
+ }
+
+ if(CommandLineOptions.Clo.StagedHoudiniMergeIgnoredAnnotations) {
+ MergeIgnoredAnnotations();
+ }
+
+ }
+
+ private void MergeIgnoredAnnotations()
+ {
+ var IgnoredAnnotationsToVariables = new Dictionary<string, HashSet<Variable>>();
+ foreach(var c in AllAnnotationIdentifiers()) {
+ IgnoredAnnotationsToVariables[c] = new HashSet<Variable>();
+ }
+ foreach(var ci in AnnotationInstances()) {
+ if(!IgnoredAnnotationsToVariables.ContainsKey(ci.AnnotationIdentifier)) {
+ continue;
+ }
+ VariableCollector vc = new VariableCollector();
+ vc.Visit(ci.Expr);
+ if(vc.usedVars.Select(Item => varDepAnalyser.VariableRelevantToAnalysis(Item, ci.Proc)).Count() != 0) {
+ continue;
+ }
+ foreach(var v in vc.usedVars) {
+ if(varDepAnalyser.Ignoring(v, ci.Proc)) {
+ IgnoredAnnotationsToVariables[ci.AnnotationIdentifier].Add(v);
+ }
+ }
+ }
+ foreach(var c in IgnoredAnnotationsToVariables.Keys) {
+ foreach(var d in IgnoredAnnotationsToVariables.Keys) {
+ if(c == d) {
+ continue;
+ }
+ if(IgnoredAnnotationsToVariables[c].Equals(IgnoredAnnotationsToVariables[d])) {
+ AnnotationDependences.AddEdge(c, d);
+ }
+ }
+ }
+ }
+
+
+
+ private void DetermineAnnotationVariableDependences()
+ {
+ if (CommandLineOptions.Clo.Trace)
+ {
+ Console.WriteLine("Annotation dependence analysis: Working out what annotations depend on");
+ }
+ annotationDependsOn = new Dictionary<string, HashSet<VariableDescriptor>>();
+ variableDirectlyReferredToByAnnotations = new Dictionary<VariableDescriptor, HashSet<string>>();
+ foreach (var c in AllAnnotationIdentifiers())
+ {
+ annotationDependsOn[c] = new HashSet<VariableDescriptor>();
+ }
+
+ foreach(var annotationInstance in AnnotationInstances()) {
+ AddDependences(annotationInstance);
+ }
+
+ }
+
+ private IEnumerable<AnnotationInstance> AnnotationInstances()
+ {
+ foreach (var impl in prog.Implementations)
+ {
+ foreach (PredicateCmd p in impl.Blocks.SelectMany(Item => Item.Cmds).OfType<PredicateCmd>())
+ {
+ string c;
+ if(Houdini.MatchCandidate(p.Expr, CandidateIdentifiers, out c)) {
+ yield return new AnnotationInstance(c, impl.Name, p.Expr);
+ } else if((p is AssertCmd) && QKeyValue.FindBoolAttribute(p.Attributes, "originated_from_invariant")) {
+ var tag = GetTagFromNonCandidateAttributes(p.Attributes);
+ if (tag != null) {
+ yield return new AnnotationInstance(tag, impl.Name, p.Expr);
+ }
+ }
+ }
+ }
+
+ foreach (var proc in prog.NonInlinedProcedures())
+ {
+ foreach (Requires r in proc.Requires)
+ {
+ string c;
+ if(Houdini.MatchCandidate(r.Condition, CandidateIdentifiers, out c)) {
+ yield return new AnnotationInstance(c, proc.Name, r.Condition);
+ } else {
+ var tag = GetTagFromNonCandidateAttributes(r.Attributes);
+ if (tag != null) {
+ yield return new AnnotationInstance(tag, proc.Name, r.Condition);
+ }
+ }
+ }
+ foreach (Ensures e in proc.Ensures)
+ {
+ string c;
+ if(Houdini.MatchCandidate(e.Condition, CandidateIdentifiers, out c)) {
+ yield return new AnnotationInstance(c, proc.Name, e.Condition);
+ } else {
+ var tag = GetTagFromNonCandidateAttributes(e.Attributes);
+ if (tag != null) {
+ yield return new AnnotationInstance(tag, proc.Name, e.Condition);
+ }
+ }
+ }
+ }
+ }
+
+ internal static string GetTagFromNonCandidateAttributes(QKeyValue Attributes)
+ {
+ string tag = QKeyValue.FindStringAttribute(Attributes, "staged_houdini_tag");
+ return tag;
+ }
+
+ private bool FindInDAG(Graph<SCC<string>> DAG, SCC<string> toFind, SCC<string> start) {
+ if (toFind == start) {
+ return true;
+ }
+ foreach (var n in DAG.Successors(start)) {
+ if (FindInDAG(DAG, toFind, n)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void AddDependences(AnnotationInstance ci) {
+ VariableCollector vc = new VariableCollector();
+ vc.VisitExpr(ci.Expr);
+
+ foreach (var v in vc.usedVars.Where(Item => varDepAnalyser.VariableRelevantToAnalysis(Item, ci.Proc))) {
+ VariableDescriptor vd =
+ varDepAnalyser.MakeDescriptor(ci.Proc, v);
+ if (!variableDirectlyReferredToByAnnotations.ContainsKey(vd)) {
+ variableDirectlyReferredToByAnnotations[vd] = new HashSet<string>();
+ }
+ variableDirectlyReferredToByAnnotations[vd].Add(ci.AnnotationIdentifier);
+
+ foreach (var w in varDepAnalyser.DependsOn(vd)) {
+ annotationDependsOn[ci.AnnotationIdentifier].Add(w);
+ }
+ }
+ }
+
+ private bool IsStageDependence(SCC<string> Src, SCC<string> Dst) {
+ foreach (var c in Src) {
+ foreach (var d in AnnotationDependences.Successors(c)) {
+ if (Dst.Contains(d)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+
+ public void dump() {
+
+ if(CommandLineOptions.Clo.DebugStagedHoudini) {
+ varDepAnalyser.dump();
+
+ Console.WriteLine("Annotations and the variables they depend on");
+ Console.WriteLine("============================================");
+ foreach (var entry in annotationDependsOn) {
+ Console.WriteLine(entry.Key + " <- ");
+ foreach (var vd in entry.Value) {
+ Console.WriteLine(" " + vd + ", ");
+ }
+ }
+
+ Console.WriteLine("");
+
+ Console.WriteLine("Variables and the annotations that directly refer to them");
+ Console.WriteLine("========================================================");
+ foreach (var entry in variableDirectlyReferredToByAnnotations) {
+ Console.WriteLine(entry.Key + " <- ");
+ foreach (var annotation in entry.Value) {
+ Console.WriteLine(" " + annotation + ", ");
+ }
+ }
+
+ Console.WriteLine("");
+
+ Console.WriteLine("Annotation dependence graph");
+ Console.WriteLine("==========================");
+ foreach (var c in AnnotationDependences.Nodes) {
+ Console.WriteLine(c + " <- ");
+ foreach (var d in AnnotationDependences.Successors(c)) {
+ Console.WriteLine(" " + d);
+ }
+ }
+ }
+
+ Console.WriteLine("");
+
+ Console.WriteLine("Strongly connected components");
+ Console.WriteLine("=============================");
+
+ List<SCC<string>> Components = StagesDAG.TopologicalSort().ToList();
+
+ for (int i = 0; i < Components.Count(); i++) {
+ Console.Write(i + ": ");
+ DumpSCC(Components[i]);
+ Console.WriteLine(); Console.WriteLine();
+ }
+
+ Console.WriteLine("Stages DAG");
+ Console.WriteLine("==========");
+ for (int i = 0; i < Components.Count(); i++) {
+ Console.Write(i + " -> { ");
+ bool first = true;
+ foreach (var d in StagesDAG.Successors(Components[i])) {
+ if (first) {
+ first = false;
+ }
+ else {
+ Console.Write(", ");
+ }
+ Console.Write(Components.IndexOf(d));
+ }
+ Console.WriteLine(" }");
+ }
+
+ }
+
+ private static void DumpSCC(SCC<string> component) {
+ var sortedComponent = component.ToList();
+ sortedComponent.Sort();
+ Console.Write("{ ");
+ bool first = true;
+ foreach (var s in sortedComponent) {
+ if (first) {
+ first = false;
+ }
+ else {
+ Console.Write(", ");
+ }
+ Console.Write(s);
+ }
+ Console.Write(" }");
+ }
+
+ private bool OnlyRefersToConstants(Expr e) {
+ VariableCollector vc = new VariableCollector();
+ vc.Visit(e);
+ return vc.usedVars.OfType<Constant>().Count() == vc.usedVars.Count();
+ }
+
+ private IEnumerable<string> GetNonCandidateAnnotations() {
+ var Result = new HashSet<string>();
+ int Counter = 0;
+ foreach(var Assertion in prog.Blocks().SelectMany(Item => Item.Cmds).
+ OfType<AssertCmd>()) {
+
+ string unused;
+ if (Houdini.MatchCandidate(Assertion.Expr, CandidateIdentifiers, out unused)) {
+ continue;
+ }
+
+ if (!QKeyValue.FindBoolAttribute(Assertion.Attributes, "originated_from_invariant")) {
+ continue;
+ }
+
+ if (OnlyRefersToConstants(Assertion.Expr)) {
+ continue;
+ }
+
+ string Tag = "staged_houdini_tag_" + Counter;
+ Result.Add(Tag);
+ Assertion.Attributes = new QKeyValue(Token.NoToken, "staged_houdini_tag",
+ new List<object> { Tag }, Assertion.Attributes);
+ Counter++;
+ }
+
+ foreach(var Req in prog.NonInlinedProcedures().SelectMany(Item => Item.Requires)) {
+
+ string unused;
+ if (Houdini.MatchCandidate(Req.Condition, CandidateIdentifiers, out unused)) {
+ continue;
+ }
+
+ if (OnlyRefersToConstants(Req.Condition)) {
+ continue;
+ }
+
+ string Tag = "staged_houdini_tag_" + Counter;
+ Result.Add(Tag);
+ Req.Attributes = new QKeyValue(Token.NoToken, "staged_houdini_tag",
+ new List<object> { Tag }, Req.Attributes);
+ Counter++;
+
+ }
+
+ foreach(var Ens in prog.NonInlinedProcedures().SelectMany(Item => Item.Ensures)) {
+
+ string unused;
+ if (Houdini.MatchCandidate(Ens.Condition, CandidateIdentifiers, out unused)) {
+ continue;
+ }
+
+ if (OnlyRefersToConstants(Ens.Condition)) {
+ continue;
+ }
+
+ string Tag = "staged_houdini_tag_" + Counter;
+ Result.Add(Tag);
+ Ens.Attributes = new QKeyValue(Token.NoToken, "staged_houdini_tag",
+ new List<object> { Tag }, Ens.Attributes);
+ Counter++;
+
+ }
+
+ return Result;
+ }
+
+ private IEnumerable<string> GetCandidates() {
+ return prog.Variables.Where(Item =>
+ QKeyValue.FindBoolAttribute(Item.Attributes, "existential")).Select(Item => Item.Name);
+ }
+
+
+ public StagedHoudiniPlan ApplyStages() {
+
+ if (NoStages())
+ {
+ Debug.Assert(false);
+ var TrivialGraph = new Graph<ScheduledStage>();
+ TrivialGraph.AddSource(new ScheduledStage(0, new HashSet<string>()));
+ return new StagedHoudiniPlan(TrivialGraph);
+ }
+
+ #region Assign annotations to stages at a given level of granularity
+
+ switch(CommandLineOptions.Clo.StagedHoudini) {
+ case COARSE_STAGES:
+ Plan = ComputeCoarseStages();
+ break;
+ case FINE_STAGES:
+ Plan = ComputeFineStages();
+ break;
+ case BALANCED_STAGES:
+ Plan = ComputeBalancedStages();
+ break;
+ default:
+ Debug.Assert(false);
+ Plan = null;
+ break;
+ }
+
+ foreach(var c in AllAnnotationIdentifiers()) {
+ Debug.Assert(Plan.StageForAnnotation(c) != null);
+ }
+ #endregion
+
+ #region Generate boolean variables to control stages
+ var stageToActiveBoolean = new Dictionary<int, Constant>();
+ var stageToCompleteBoolean = new Dictionary<int, Constant>();
+
+ foreach (var stage in Plan)
+ {
+ var stageActive = new Constant(Token.NoToken,
+ new TypedIdent(Token.NoToken, "_stage_" + stage.GetId() + "_active", Type.Bool),
+ false);
+ stageActive.AddAttribute("stage_active", new object[] { new LiteralExpr(Token.NoToken, BigNum.FromInt(stage.GetId())) });
+ prog.AddTopLevelDeclaration(stageActive);
+ stageToActiveBoolean[stage.GetId()] = stageActive;
+
+ var stageComplete = new Constant(Token.NoToken,
+ new TypedIdent(Token.NoToken, "_stage_" + stage.GetId() + "_complete", Type.Bool),
+ false);
+ stageComplete.AddAttribute("stage_complete", new object[] { new LiteralExpr(Token.NoToken, BigNum.FromInt(stage.GetId())) });
+ prog.AddTopLevelDeclaration(stageComplete);
+ stageToCompleteBoolean[stage.GetId()] = stageComplete;
+ }
+ #endregion
+
+ #region Adapt annotation assertions to take account of stages
+ foreach (var b in prog.Implementations.Select(Item => Item.Blocks).SelectMany(Item => Item))
+ {
+ List<Cmd> newCmds = new List<Cmd>();
+ foreach (var cmd in b.Cmds)
+ {
+ var a = cmd as AssertCmd;
+ string c;
+ if (a != null) {
+ if (Houdini.MatchCandidate(a.Expr, CandidateIdentifiers, out c))
+ {
+ newCmds.Add(new AssertCmd(a.tok, Houdini.AddConditionToCandidate(a.Expr,
+ Expr.Ident(stageToActiveBoolean[Plan.StageForAnnotation(c).GetId()]), c), a.Attributes));
+ newCmds.Add(new AssumeCmd(a.tok, Houdini.AddConditionToCandidate(a.Expr,
+ Expr.Ident(stageToCompleteBoolean[Plan.StageForAnnotation(c).GetId()]), c), a.Attributes));
+ } else if (QKeyValue.FindBoolAttribute(a.Attributes, "originated_from_invariant")) {
+ string tag = GetTagFromNonCandidateAttributes(a.Attributes);
+ if (tag == null) {
+ newCmds.Add(a);
+ } else {
+ newCmds.Add(new AssertCmd(a.tok, Expr.Imp(
+ Expr.Ident(stageToActiveBoolean[Plan.StageForAnnotation(tag).GetId()]), a.Expr),
+ a.Attributes));
+ newCmds.Add(new AssumeCmd(a.tok, Expr.Imp(
+ Expr.Ident(stageToCompleteBoolean[Plan.StageForAnnotation(tag).GetId()]), a.Expr),
+ a.Attributes));
+ }
+ }
+ }
+ else
+ {
+ newCmds.Add(cmd);
+ }
+ }
+ b.Cmds = newCmds;
+ }
+ #endregion
+
+ #region Adapt pre/postconditions to take account of stages
+ foreach (var p in prog.NonInlinedProcedures())
+ {
+
+ #region Handle the preconditions
+ {
+ List<Requires> newRequires = new List<Requires>();
+ foreach(Requires r in p.Requires) {
+ string c;
+ if (Houdini.MatchCandidate(r.Condition, CandidateIdentifiers, out c)) {
+ newRequires.Add(new Requires(r.tok, false,
+ Houdini.AddConditionToCandidate(r.Condition,
+ Expr.Ident(stageToActiveBoolean[Plan.StageForAnnotation(c).GetId()]), c),
+ r.Comment, r.Attributes));
+ newRequires.Add(new Requires(r.tok, true,
+ Houdini.AddConditionToCandidate(r.Condition,
+ Expr.Ident(stageToCompleteBoolean[Plan.StageForAnnotation(c).GetId()]), c),
+ r.Comment, r.Attributes));
+ } else {
+ string tag = GetTagFromNonCandidateAttributes(r.Attributes);
+ if (tag == null) {
+ newRequires.Add(r);
+ } else {
+ newRequires.Add(new Requires(r.tok, false,
+ Expr.Imp(Expr.Ident(stageToActiveBoolean[Plan.StageForAnnotation(tag).GetId()]), r.Condition),
+ r.Comment, r.Attributes));
+ newRequires.Add(new Requires(r.tok, true,
+ Expr.Imp(Expr.Ident(stageToCompleteBoolean[Plan.StageForAnnotation(tag).GetId()]), r.Condition),
+ r.Comment, r.Attributes));
+ }
+ }
+ }
+ p.Requires = newRequires;
+ }
+ #endregion
+
+ #region Handle the postconditions
+ {
+ List<Ensures> newEnsures = new List<Ensures>();
+ foreach(Ensures e in p.Ensures) {
+ string c;
+ if (Houdini.MatchCandidate(e.Condition, CandidateIdentifiers, out c)) {
+ int stage = Plan.StageForAnnotation(c).GetId();
+ newEnsures.Add(new Ensures(e.tok, false,
+ Houdini.AddConditionToCandidate(e.Condition,
+ Expr.Ident(stageToActiveBoolean[stage]), c),
+ e.Comment, e.Attributes));
+ newEnsures.Add(new Ensures(e.tok, true,
+ Houdini.AddConditionToCandidate(e.Condition,
+ Expr.Ident(stageToCompleteBoolean[stage]), c),
+ e.Comment, e.Attributes));
+ } else {
+ string tag = GetTagFromNonCandidateAttributes(e.Attributes);
+ if (tag == null) {
+ newEnsures.Add(e);
+ } else {
+ newEnsures.Add(new Ensures(e.tok, false,
+ Expr.Imp(Expr.Ident(stageToActiveBoolean[Plan.StageForAnnotation(tag).GetId()]), e.Condition),
+ e.Comment, e.Attributes));
+ newEnsures.Add(new Ensures(e.tok, true,
+ Expr.Imp(Expr.Ident(stageToCompleteBoolean[Plan.StageForAnnotation(tag).GetId()]), e.Condition),
+ e.Comment, e.Attributes));
+ }
+ }
+ }
+ p.Ensures = newEnsures;
+ }
+ #endregion
+
+ }
+ #endregion
+
+ return Plan;
+
+ }
+
+ private int FindLargestStage() {
+ return StagesDAG.Nodes.Select(Item => Item.Count()).Max();
+ }
+
+
+ private StagedHoudiniPlan ComputeCoarseStages()
+ {
+ foreach(var n in StagesDAG.Nodes) {
+ Debug.Assert(!StagesDAG.Successors(n).Contains(n));
+ }
+
+ Graph<ScheduledStage> Dependences = new Graph<ScheduledStage>();
+
+ var done = new Dictionary<SCC<string>, ScheduledStage>();
+ done[GetStartNodeOfStagesDAG()] = new ScheduledStage(0, new HashSet<string>());
+
+ for(int stageId = 1; done.Count() != StagesDAG.Nodes.Count(); stageId++)
+ {
+ var Stage = new ScheduledStage(stageId, new HashSet<string>());
+ HashSet<SCC<string>> AssignedToThisStage = new HashSet<SCC<string>>();
+
+ foreach (var n in StagesDAG.Nodes.Where(Item => !done.ContainsKey(Item)))
+ {
+ if(StagesDAG.Successors(n).Where(Item => !done.ContainsKey(Item)).Count() == 0) {
+ foreach(var s in StagesDAG.Successors(n)) {
+ Debug.Assert(s != n);
+ Debug.Assert(Stage != done[s]);
+ Dependences.AddEdge(Stage, done[s]);
+ }
+ foreach (var a in n)
+ {
+ Stage.AddAnnotation(a);
+ }
+ AssignedToThisStage.Add(n);
+ }
+ }
+
+ foreach(var n in AssignedToThisStage) {
+ done[n] = Stage;
+ }
+ }
+ return new StagedHoudiniPlan(Dependences);
+ }
+
+ private StagedHoudiniPlan ComputeBalancedStages()
+ {
+ Graph<ScheduledStage> Dependences = new Graph<ScheduledStage>();
+ var done = new Dictionary<SCC<string>, ScheduledStage>();
+ done[GetStartNodeOfStagesDAG()] = new ScheduledStage(0, new HashSet<string>());
+
+ int maxStageSize = 200;
+
+ for(int stageId = 1; done.Count() != StagesDAG.Nodes.Count(); stageId++)
+ {
+ int stageSize = 0;
+ ScheduledStage Stage = new ScheduledStage(stageId, new HashSet<string>());
+ HashSet<SCC<string>> AddedToThisStage = new HashSet<SCC<string>>();
+
+ foreach (var n in StagesDAG.Nodes.Where(Item => !done.ContainsKey(Item)))
+ {
+ if(stageSize + n.Count() > maxStageSize) {
+ continue;
+ }
+ if(StagesDAG.Successors(n).Where(Item => !done.ContainsKey(Item)).Count() == 0) {
+ foreach (var c in n)
+ {
+ Stage.AddAnnotation(c);
+ stageSize++;
+ }
+ foreach(var s in StagesDAG.Successors(n)) {
+ Dependences.AddEdge(Stage, done[s]);
+ }
+ AddedToThisStage.Add(n);
+ }
+ }
+ foreach(var n in AddedToThisStage) {
+ done[n] = Stage;
+ }
+ if(stageSize == 0) {
+ maxStageSize *= 2;
+ }
+ }
+ return new StagedHoudiniPlan(Dependences);
+ }
+
+ private StagedHoudiniPlan ComputeFineStages()
+ {
+ Graph<ScheduledStage> Dependences = new Graph<ScheduledStage>();
+ var done = new Dictionary<SCC<string>, ScheduledStage>();
+
+ List<SCC<string>> components = StagesDAG.TopologicalSort().ToList();
+ components.Reverse();
+
+ for (int i = 0; i < components.Count(); i++)
+ {
+ ScheduledStage Stage = new ScheduledStage(i, new HashSet<string>());
+ done[components[i]] = Stage;
+ foreach (var c in components[i])
+ {
+ Stage.AddAnnotation(c);
+ }
+ foreach(var s in StagesDAG.Successors(components[i])) {
+ Dependences.AddEdge(Stage, done[s]);
+ }
+ }
+ return new StagedHoudiniPlan(Dependences);
+ }
+
+ private SCC<string> GetStartNodeOfStagesDAG()
+ {
+ return StagesDAG.Nodes.Where(Item => StagesDAG.Successors(Item).Count() == 0).
+ ToList()[0];
+ }
+
+ private bool NoStages()
+ {
+ return AllAnnotationIdentifiers().Count() == 0 || StagesDAG.Nodes.Count() == 0;
+ }
+ }
+
+ interface IAnnotationReachabilityChecker {
+ bool MayReach(string c, string d);
+ }
+
+ class DummyAnnotationReachabilityChecker : IAnnotationReachabilityChecker {
+ public bool MayReach(string c, string d) {
+ return true;
+ }
+ }
+
+ class AnnotationReachabilityChecker : IAnnotationReachabilityChecker {
+
+ private enum PrePost {
+ PRE, POST
+ }
+
+ private Program prog;
+ private IEnumerable<string> AnnotationIdentifiers;
+ private IInterproceduralReachabilityGraph reachabilityGraph;
+ private Dictionary<string, HashSet<object>> annotationToOccurences;
+
+ internal AnnotationReachabilityChecker(Program prog, IEnumerable<string> AnnotationIdentifiers) {
+ this.prog = prog;
+ this.AnnotationIdentifiers = AnnotationIdentifiers;
+ this.reachabilityGraph = new InterproceduralReachabilityGraph(prog);
+ this.annotationToOccurences = new Dictionary<string,HashSet<object>>();
+
+ // Add all annotation occurrences in blocks
+ foreach(Block b in prog.Blocks()) {
+ foreach(var assertCmd in b.Cmds.OfType<AssertCmd>()) {
+ string c;
+ if(Houdini.MatchCandidate(assertCmd.Expr, AnnotationIdentifiers, out c)) {
+ AddAnnotationOccurrence(c, b);
+ } else {
+ var tag = AnnotationDependenceAnalyser.GetTagFromNonCandidateAttributes(assertCmd.Attributes);
+ if (tag != null) {
+ AddAnnotationOccurrence(tag, b);
+ }
+ }
+ }
+ }
+
+ // Add all annotation occurrences in pre and post conditions
+ foreach(var proc in prog.NonInlinedProcedures()) {
+ foreach(Requires r in proc.Requires) {
+ string c;
+ if(Houdini.MatchCandidate(r.Condition, AnnotationIdentifiers, out c)) {
+ AddAnnotationOccurrence(c, new Tuple<string, PrePost>(proc.Name, PrePost.PRE));
+ } else {
+ string tag = AnnotationDependenceAnalyser.GetTagFromNonCandidateAttributes(r.Attributes);
+ if(tag != null) {
+ AddAnnotationOccurrence(tag, new Tuple<string, PrePost>(proc.Name, PrePost.PRE));
+ }
+ }
+ }
+ foreach(Ensures e in proc.Ensures) {
+ string c;
+ if(Houdini.MatchCandidate(e.Condition, AnnotationIdentifiers, out c)) {
+ AddAnnotationOccurrence(c, new Tuple<string, PrePost>(proc.Name, PrePost.POST));
+ } else {
+ string tag = AnnotationDependenceAnalyser.GetTagFromNonCandidateAttributes(e.Attributes);
+ if(tag != null) {
+ AddAnnotationOccurrence(tag, new Tuple<string, PrePost>(proc.Name, PrePost.PRE));
+ }
+ }
+ }
+ }
+
+ }
+
+ private void AddAnnotationOccurrence(string c, object o) {
+ Debug.Assert(o is Block || o is Tuple<string, PrePost>);
+ if(!annotationToOccurences.ContainsKey(c)) {
+ annotationToOccurences[c] = new HashSet<object>();
+ }
+ annotationToOccurences[c].Add(o);
+ }
+
+ public bool MayReach(string c, string d) {
+ foreach(object cOccurrence in annotationToOccurences[c]) {
+ foreach(object dOccurrence in annotationToOccurences[d]) {
+ if(OccurrencesMayReach(cOccurrence, dOccurrence)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private bool OccurrencesMayReach(object cOccurrence, object dOccurrence) {
+ Debug.Assert(cOccurrence is Block || cOccurrence is Tuple<string, PrePost>);
+ Debug.Assert(dOccurrence is Block || dOccurrence is Tuple<string, PrePost>);
+
+ Block cInterproceduralBlock = GetInterproceduralBlock(cOccurrence);
+ Block dInterproceduralBlock = GetInterproceduralBlock(dOccurrence);
+
+ return reachabilityGraph.MayReach(cInterproceduralBlock, dInterproceduralBlock);
+
+ }
+
+ private Block GetInterproceduralBlock(object cOccurrence)
+ {
+ Debug.Assert(cOccurrence is Block || cOccurrence is Tuple<string, PrePost>);
+
+ var stringPrePostPair = cOccurrence as Tuple<string, PrePost>;
+ if(stringPrePostPair != null) {
+ if(stringPrePostPair.Item2 == PrePost.PRE) {
+ return reachabilityGraph.GetNewEntryBlock(stringPrePostPair.Item1);
+ } else {
+ return reachabilityGraph.GetNewExitBlock(stringPrePostPair.Item1);
+ }
+ }
+
+ return reachabilityGraph.GetNewBlock((Block)cOccurrence);
+
+ }
+ }
+
+ class AnnotationInstance {
+ public string AnnotationIdentifier;
+ public string Proc;
+ public Expr Expr;
+
+ internal AnnotationInstance(string AnnotationIdentifier, string Proc, Expr Expr) {
+ this.AnnotationIdentifier = AnnotationIdentifier;
+ this.Proc = Proc;
+ this.Expr = Expr;
+ }
+ }
+
+}
diff --git a/Source/Houdini/Checker.cs b/Source/Houdini/Checker.cs
index d9da29cc..73842019 100644
--- a/Source/Houdini/Checker.cs
+++ b/Source/Houdini/Checker.cs
@@ -1,457 +1,457 @@
-//-----------------------------------------------------------------------------
-//
-// Copyright (C) Microsoft Corporation. All Rights Reserved.
-//
-//-----------------------------------------------------------------------------
-using System;
-using System.Diagnostics.Contracts;
-using System.Collections.Generic;
-using Microsoft.Boogie;
-using Microsoft.Boogie.VCExprAST;
-using Microsoft.Basetypes;
-using System.Collections;
-using System.IO;
-using System.Threading;
-using VC;
-using System.Linq;
-
-namespace Microsoft.Boogie.Houdini {
- public class ExistentialConstantCollector : ReadOnlyVisitor {
- public static void CollectHoudiniConstants(Houdini houdini, Implementation impl, out ExistentialConstantCollector collector)
- {
- collector = new ExistentialConstantCollector(houdini);
- collector.impl = impl;
- collector.VisitImplementation(impl);
- }
-
- private ExistentialConstantCollector(Houdini houdini) {
- this.houdini = houdini;
- this.houdiniAssertConstants = new HashSet<Variable>();
- this.houdiniAssumeConstants = new HashSet<Variable>();
-
- this.explainNegative = new HashSet<Variable>();
- this.explainPositive = new HashSet<Variable>();
- this.constToControl = new Dictionary<string, Tuple<Variable, Variable>>();
- }
- private Houdini houdini;
- public HashSet<Variable> houdiniAssertConstants;
- public HashSet<Variable> houdiniAssumeConstants;
-
- // Explain Houdini stuff
- public HashSet<Variable> explainPositive;
- public HashSet<Variable> explainNegative;
- public Dictionary<string, Tuple<Variable, Variable>> constToControl;
- Implementation impl;
-
- public override Cmd VisitAssertRequiresCmd(AssertRequiresCmd node) {
- AddHoudiniConstant(node);
- return base.VisitAssertRequiresCmd(node);
- }
- public override Cmd VisitAssertEnsuresCmd(AssertEnsuresCmd node) {
- AddHoudiniConstant(node);
- return base.VisitAssertEnsuresCmd(node);
- }
- public override Cmd VisitAssertCmd(AssertCmd node) {
- AddHoudiniConstant(node);
- return base.VisitAssertCmd(node);
- }
- public override Cmd VisitAssumeCmd(AssumeCmd node) {
- AddHoudiniConstant(node);
- return base.VisitAssumeCmd(node);
- }
- private void AddHoudiniConstant(AssertCmd assertCmd)
- {
- Variable houdiniConstant;
- if (houdini.MatchCandidate(assertCmd.Expr, out houdiniConstant))
- houdiniAssertConstants.Add(houdiniConstant);
-
- if (houdiniConstant != null && CommandLineOptions.Clo.ExplainHoudini && !constToControl.ContainsKey(houdiniConstant.Name))
- {
- // For each houdini constant c, create two more constants c_pos and c_neg.
- // Then change the asserted condition (c ==> \phi) to
- // (c ==> (c_pos && (\phi || \not c_neg))
- var control = createNewExplainConstants(houdiniConstant);
- assertCmd.Expr = houdini.InsertCandidateControl(assertCmd.Expr, control.Item1, control.Item2);
- explainPositive.Add(control.Item1);
- explainNegative.Add(control.Item2);
- constToControl.Add(houdiniConstant.Name, control);
- }
- }
- private void AddHoudiniConstant(AssumeCmd assumeCmd)
- {
- Variable houdiniConstant;
- if (houdini.MatchCandidate(assumeCmd.Expr, out houdiniConstant))
- houdiniAssumeConstants.Add(houdiniConstant);
- }
- private Tuple<Variable, Variable> createNewExplainConstants(Variable v)
- {
- Contract.Assert(impl != null);
- Contract.Assert(CommandLineOptions.Clo.ExplainHoudini);
- Variable v1 = new Constant(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("{0}_{1}_{2}", v.Name, impl.Name, "pos"), Microsoft.Boogie.BasicType.Bool));
- Variable v2 = new Constant(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("{0}_{1}_{2}", v.Name, impl.Name, "neg"), Microsoft.Boogie.BasicType.Bool));
-
- return Tuple.Create(v1, v2);
- }
- }
-
-
-
- public class HoudiniSession {
-
- public class HoudiniStatistics {
- public double proverTime = 0;
- public int numProverQueries = 0;
- public double unsatCoreProverTime = 0;
- public int numUnsatCoreProverQueries = 0;
- public int numUnsatCorePrunings = 0;
- }
-
- public string descriptiveName;
- public HoudiniStatistics stats;
- private VCExpr conjecture;
- private ProverInterface.ErrorHandler handler;
- ConditionGeneration.CounterexampleCollector collector;
- HashSet<Variable> unsatCoreSet;
- HashSet<Variable> houdiniConstants;
- public HashSet<Variable> houdiniAssertConstants;
- private HashSet<Variable> houdiniAssumeConstants;
-
- // Extra constants created for ExplainHoudini
- private HashSet<Variable> explainConstantsPositive;
- private HashSet<Variable> explainConstantsNegative;
- private Dictionary<string, Tuple<Variable, Variable>> constantToControl;
-
- public bool InUnsatCore(Variable constant) {
- if (unsatCoreSet == null)
- return true;
- if (unsatCoreSet.Contains(constant))
- return true;
- stats.numUnsatCorePrunings++;
- return false;
- }
-
- public HoudiniSession(Houdini houdini, VCGen vcgen, ProverInterface proverInterface, Program program, Implementation impl, HoudiniStatistics stats, int taskID = -1) {
- this.descriptiveName = impl.Name;
- this.stats = stats;
- collector = new ConditionGeneration.CounterexampleCollector();
- collector.OnProgress("HdnVCGen", 0, 0, 0.0);
-
- vcgen.ConvertCFG2DAG(impl, taskID: taskID);
- ModelViewInfo mvInfo;
- var gotoCmdOrigins = vcgen.PassifyImpl(impl, out mvInfo);
-
- ExistentialConstantCollector ecollector;
- ExistentialConstantCollector.CollectHoudiniConstants(houdini, impl, out ecollector);
- this.houdiniAssertConstants = ecollector.houdiniAssertConstants;
- this.houdiniAssumeConstants = ecollector.houdiniAssumeConstants;
- this.explainConstantsNegative = ecollector.explainNegative;
- this.explainConstantsPositive = ecollector.explainPositive;
- this.constantToControl = ecollector.constToControl;
-
- houdiniConstants = new HashSet<Variable>();
- houdiniConstants.UnionWith(houdiniAssertConstants);
- houdiniConstants.UnionWith(houdiniAssumeConstants);
-
- var exprGen = proverInterface.Context.ExprGen;
- VCExpr controlFlowVariableExpr = CommandLineOptions.Clo.UseLabels ? null : exprGen.Integer(BigNum.ZERO);
-
- Dictionary<int, Absy> label2absy;
- conjecture = vcgen.GenerateVC(impl, controlFlowVariableExpr, out label2absy, proverInterface.Context);
- 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)));
- conjecture = exprGen.Implies(eqExpr, conjecture);
- }
-
- Macro macro = new Macro(Token.NoToken, descriptiveName, new List<Variable>(), new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", Type.Bool), false));
- proverInterface.DefineMacro(macro, conjecture);
- conjecture = exprGen.Function(macro);
-
- if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Local) {
- handler = new VCGen.ErrorReporterLocal(gotoCmdOrigins, label2absy, impl.Blocks, vcgen.incarnationOriginMap, collector, mvInfo, proverInterface.Context, program);
- }
- else {
- handler = new VCGen.ErrorReporter(gotoCmdOrigins, label2absy, impl.Blocks, vcgen.incarnationOriginMap, collector, mvInfo, proverInterface.Context, program);
- }
-
- }
-
- private VCExpr BuildAxiom(ProverInterface proverInterface, Dictionary<Variable, bool> currentAssignment) {
- ProverContext proverContext = proverInterface.Context;
- Boogie2VCExprTranslator exprTranslator = proverContext.BoogieExprTranslator;
- VCExpressionGenerator exprGen = proverInterface.VCExprGen;
-
- VCExpr expr = VCExpressionGenerator.True;
-
- foreach (KeyValuePair<Variable, bool> kv in currentAssignment) {
- Variable constant = kv.Key;
- VCExprVar exprVar = exprTranslator.LookupVariable(constant);
- if (kv.Value) {
- expr = exprGen.And(expr, exprVar);
- }
- else {
- expr = exprGen.And(expr, exprGen.Not(exprVar));
- }
- }
-
- if (CommandLineOptions.Clo.ExplainHoudini)
- {
- // default values for ExplainHoudini control variables
- foreach (var constant in explainConstantsNegative.Concat(explainConstantsPositive))
- {
- expr = exprGen.And(expr, exprTranslator.LookupVariable(constant));
- }
- }
-
- /*
- foreach (Variable constant in this.houdiniConstants) {
- VCExprVar exprVar = exprTranslator.LookupVariable(constant);
- if (currentAssignment[constant]) {
- expr = exprGen.And(expr, exprVar);
- }
- else {
- expr = exprGen.And(expr, exprGen.Not(exprVar));
- }
- }
- */
-
- if(CommandLineOptions.Clo.Trace) {
- Console.WriteLine("Houdini assignment axiom: " + expr);
- }
-
- return expr;
- }
-
- public ProverInterface.Outcome Verify(ProverInterface proverInterface, Dictionary<Variable, bool> assignment, out List<Counterexample> errors, int taskID = -1) {
- collector.examples.Clear();
-
- if (CommandLineOptions.Clo.Trace) {
- Console.WriteLine("Verifying " + descriptiveName);
- }
- DateTime now = DateTime.UtcNow;
-
- VCExpr vc = proverInterface.VCExprGen.Implies(BuildAxiom(proverInterface, assignment), conjecture);
- proverInterface.BeginCheck(descriptiveName, vc, handler);
- ProverInterface.Outcome proverOutcome = proverInterface.CheckOutcome(handler, taskID: taskID);
-
- double queryTime = (DateTime.UtcNow - now).TotalSeconds;
- stats.proverTime += queryTime;
- stats.numProverQueries++;
- if (CommandLineOptions.Clo.Trace) {
- Console.WriteLine("Outcome = " + proverOutcome);
- Console.WriteLine("Time taken = " + queryTime);
- }
-
- errors = collector.examples;
- return proverOutcome;
- }
-
- // MAXSAT
- public void Explain(ProverInterface proverInterface,
- Dictionary<Variable, bool> assignment, Variable refutedConstant)
- {
- Contract.Assert(CommandLineOptions.Clo.ExplainHoudini);
-
- collector.examples.Clear();
-
- // debugging
- houdiniAssertConstants.Iter(v => System.Diagnostics.Debug.Assert(assignment.ContainsKey(v)));
- houdiniAssumeConstants.Iter(v => System.Diagnostics.Debug.Assert(assignment.ContainsKey(v)));
- Contract.Assert(assignment.ContainsKey(refutedConstant));
- Contract.Assert(houdiniAssertConstants.Contains(refutedConstant));
-
- var hardAssumptions = new List<VCExpr>();
- var softAssumptions = new List<VCExpr>();
-
- Boogie2VCExprTranslator exprTranslator = proverInterface.Context.BoogieExprTranslator;
- VCExpressionGenerator exprGen = proverInterface.VCExprGen;
- var controlExpr = VCExpressionGenerator.True;
-
- foreach (var tup in assignment)
- {
- Variable constant = tup.Key;
- VCExprVar exprVar = exprTranslator.LookupVariable(constant);
- var val = tup.Value;
-
- if (houdiniAssumeConstants.Contains(constant))
- {
- if (tup.Value)
- hardAssumptions.Add(exprVar);
- else
- // Previously removed assumed candidates are the soft constraints
- softAssumptions.Add(exprVar);
- }
- else if (houdiniAssertConstants.Contains(constant))
- {
- if (constant == refutedConstant)
- hardAssumptions.Add(exprVar);
- else
- hardAssumptions.Add(exprGen.Not(exprVar));
- }
- else
- {
- if (tup.Value)
- hardAssumptions.Add(exprVar);
- else
- hardAssumptions.Add(exprGen.Not(exprVar));
- }
-
- // For an asserted condition (c ==> \phi),
- // ExplainHoudini's extra control constants (c_pos, c_neg) are used as follows:
- // (true, true): "assert \phi"
- // (false, _): "assert false"
- // (true, false): "assert true"
- if (constant != refutedConstant && constantToControl.ContainsKey(constant.Name))
- {
- var posControl = constantToControl[constant.Name].Item1;
- var negControl = constantToControl[constant.Name].Item2;
-
- // Handle self-recursion
- if (houdiniAssertConstants.Contains(constant) && houdiniAssumeConstants.Contains(constant))
- {
- // disable this assert
- controlExpr = exprGen.And(controlExpr, exprGen.And(exprTranslator.LookupVariable(posControl), exprGen.Not(exprTranslator.LookupVariable(negControl))));
- }
- else
- {
- // default values for control variables
- controlExpr = exprGen.And(controlExpr, exprGen.And(exprTranslator.LookupVariable(posControl), exprTranslator.LookupVariable(negControl)));
- }
- }
- }
-
- hardAssumptions.Add(exprGen.Not(conjecture));
-
- // default values for control variables
- Contract.Assert(constantToControl.ContainsKey(refutedConstant.Name));
- var pc = constantToControl[refutedConstant.Name].Item1;
- var nc = constantToControl[refutedConstant.Name].Item2;
-
- var controlExprNoop = exprGen.And(controlExpr,
- exprGen.And(exprTranslator.LookupVariable(pc), exprTranslator.LookupVariable(nc)));
-
- var controlExprFalse = exprGen.And(controlExpr,
- exprGen.And(exprGen.Not(exprTranslator.LookupVariable(pc)), exprGen.Not(exprTranslator.LookupVariable(nc))));
-
- if (CommandLineOptions.Clo.Trace)
- {
- Console.WriteLine("Verifying (MaxSat) " + descriptiveName);
- }
- DateTime now = DateTime.UtcNow;
-
- var el = CommandLineOptions.Clo.ProverCCLimit;
- CommandLineOptions.Clo.ProverCCLimit = 1;
-
- var outcome = ProverInterface.Outcome.Undetermined;
-
- do
- {
- List<int> unsatisfiedSoftAssumptions;
-
- hardAssumptions.Add(controlExprNoop);
- outcome = proverInterface.CheckAssumptions(hardAssumptions, softAssumptions, out unsatisfiedSoftAssumptions, handler);
- hardAssumptions.RemoveAt(hardAssumptions.Count - 1);
-
- if (outcome == ProverInterface.Outcome.TimeOut || outcome == ProverInterface.Outcome.OutOfMemory || outcome == ProverInterface.Outcome.Undetermined)
- break;
-
- var reason = new HashSet<string>();
- unsatisfiedSoftAssumptions.Iter(i => reason.Add(softAssumptions[i].ToString()));
- if (CommandLineOptions.Clo.Trace)
- {
- Console.Write("Reason for removal of {0}: ", refutedConstant.Name);
- reason.Iter(r => Console.Write("{0} ", r));
- Console.WriteLine();
- }
-
- // Get rid of those constants from the "reason" that can even make
- // "assert false" pass
-
- hardAssumptions.Add(controlExprFalse);
- var softAssumptions2 = new List<VCExpr>();
- for (int i = 0; i < softAssumptions.Count; i++)
- {
- if (unsatisfiedSoftAssumptions.Contains(i))
- {
- softAssumptions2.Add(softAssumptions[i]);
- continue;
- }
- hardAssumptions.Add(softAssumptions[i]);
- }
-
- var unsatisfiedSoftAssumptions2 = new List<int>();
- outcome = proverInterface.CheckAssumptions(hardAssumptions, softAssumptions2, out unsatisfiedSoftAssumptions2, handler);
-
- if (outcome == ProverInterface.Outcome.TimeOut || outcome == ProverInterface.Outcome.OutOfMemory || outcome == ProverInterface.Outcome.Undetermined)
- break;
-
- unsatisfiedSoftAssumptions2.Iter(i => reason.Remove(softAssumptions2[i].ToString()));
- var reason1 = new HashSet<string>(); //these are the reasons for inconsistency
- unsatisfiedSoftAssumptions2.Iter(i => reason1.Add(softAssumptions2[i].ToString()));
-
- if (CommandLineOptions.Clo.Trace)
- {
- Console.Write("Revised reason for removal of {0}: ", refutedConstant.Name);
- reason.Iter(r => Console.Write("{0} ", r));
- Console.WriteLine();
- }
- foreach (var r in reason)
- {
- Houdini.explainHoudiniDottyFile.WriteLine("{0} -> {1} [ label = \"{2}\" color=red ];", refutedConstant.Name, r, descriptiveName);
- }
- //also add the removed reasons using dotted edges (requires- x != 0, requires- x == 0 ==> assert x != 0)
- foreach (var r in reason1)
- {
- Houdini.explainHoudiniDottyFile.WriteLine("{0} -> {1} [ label = \"{2}\" color=blue style=dotted ];", refutedConstant.Name, r, descriptiveName);
- }
- } while (false);
-
- if (outcome == ProverInterface.Outcome.TimeOut || outcome == ProverInterface.Outcome.OutOfMemory || outcome == ProverInterface.Outcome.Undetermined)
- {
- Houdini.explainHoudiniDottyFile.WriteLine("{0} -> {1} [ label = \"{2}\" color=red ];", refutedConstant.Name, "TimeOut", descriptiveName);
- }
-
- CommandLineOptions.Clo.ProverCCLimit = el;
-
- double queryTime = (DateTime.UtcNow - now).TotalSeconds;
- stats.proverTime += queryTime;
- stats.numProverQueries++;
- if (CommandLineOptions.Clo.Trace)
- {
- Console.WriteLine("Time taken = " + queryTime);
- }
- }
-
- public void UpdateUnsatCore(ProverInterface proverInterface, Dictionary<Variable, bool> assignment)
- {
- DateTime now = DateTime.UtcNow;
-
- Boogie2VCExprTranslator exprTranslator = proverInterface.Context.BoogieExprTranslator;
- proverInterface.Push();
- proverInterface.Assert(conjecture, false);
- foreach (var v in assignment.Keys) {
- if (assignment[v]) continue;
- proverInterface.Assert(exprTranslator.LookupVariable(v), false);
- }
- List<Variable> assumptionVars = new List<Variable>();
- List<VCExpr> assumptionExprs = new List<VCExpr>();
- foreach (var v in assignment.Keys) {
- if (!assignment[v]) continue;
- assumptionVars.Add(v);
- assumptionExprs.Add(exprTranslator.LookupVariable(v));
- }
- List<int> unsatCore;
- ProverInterface.Outcome tmp = proverInterface.CheckAssumptions(assumptionExprs, out unsatCore, handler);
- System.Diagnostics.Debug.Assert(tmp == ProverInterface.Outcome.Valid);
- unsatCoreSet = new HashSet<Variable>();
- foreach (int i in unsatCore)
- unsatCoreSet.Add(assumptionVars[i]);
- proverInterface.Pop();
-
- double unsatCoreQueryTime = (DateTime.UtcNow - now).TotalSeconds;
- stats.unsatCoreProverTime += unsatCoreQueryTime;
- stats.numUnsatCoreProverQueries++;
- }
-
- }
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Diagnostics.Contracts;
+using System.Collections.Generic;
+using Microsoft.Boogie;
+using Microsoft.Boogie.VCExprAST;
+using Microsoft.Basetypes;
+using System.Collections;
+using System.IO;
+using System.Threading;
+using VC;
+using System.Linq;
+
+namespace Microsoft.Boogie.Houdini {
+ public class ExistentialConstantCollector : ReadOnlyVisitor {
+ public static void CollectHoudiniConstants(Houdini houdini, Implementation impl, out ExistentialConstantCollector collector)
+ {
+ collector = new ExistentialConstantCollector(houdini);
+ collector.impl = impl;
+ collector.VisitImplementation(impl);
+ }
+
+ private ExistentialConstantCollector(Houdini houdini) {
+ this.houdini = houdini;
+ this.houdiniAssertConstants = new HashSet<Variable>();
+ this.houdiniAssumeConstants = new HashSet<Variable>();
+
+ this.explainNegative = new HashSet<Variable>();
+ this.explainPositive = new HashSet<Variable>();
+ this.constToControl = new Dictionary<string, Tuple<Variable, Variable>>();
+ }
+ private Houdini houdini;
+ public HashSet<Variable> houdiniAssertConstants;
+ public HashSet<Variable> houdiniAssumeConstants;
+
+ // Explain Houdini stuff
+ public HashSet<Variable> explainPositive;
+ public HashSet<Variable> explainNegative;
+ public Dictionary<string, Tuple<Variable, Variable>> constToControl;
+ Implementation impl;
+
+ public override Cmd VisitAssertRequiresCmd(AssertRequiresCmd node) {
+ AddHoudiniConstant(node);
+ return base.VisitAssertRequiresCmd(node);
+ }
+ public override Cmd VisitAssertEnsuresCmd(AssertEnsuresCmd node) {
+ AddHoudiniConstant(node);
+ return base.VisitAssertEnsuresCmd(node);
+ }
+ public override Cmd VisitAssertCmd(AssertCmd node) {
+ AddHoudiniConstant(node);
+ return base.VisitAssertCmd(node);
+ }
+ public override Cmd VisitAssumeCmd(AssumeCmd node) {
+ AddHoudiniConstant(node);
+ return base.VisitAssumeCmd(node);
+ }
+ private void AddHoudiniConstant(AssertCmd assertCmd)
+ {
+ Variable houdiniConstant;
+ if (houdini.MatchCandidate(assertCmd.Expr, out houdiniConstant))
+ houdiniAssertConstants.Add(houdiniConstant);
+
+ if (houdiniConstant != null && CommandLineOptions.Clo.ExplainHoudini && !constToControl.ContainsKey(houdiniConstant.Name))
+ {
+ // For each houdini constant c, create two more constants c_pos and c_neg.
+ // Then change the asserted condition (c ==> \phi) to
+ // (c ==> (c_pos && (\phi || \not c_neg))
+ var control = createNewExplainConstants(houdiniConstant);
+ assertCmd.Expr = houdini.InsertCandidateControl(assertCmd.Expr, control.Item1, control.Item2);
+ explainPositive.Add(control.Item1);
+ explainNegative.Add(control.Item2);
+ constToControl.Add(houdiniConstant.Name, control);
+ }
+ }
+ private void AddHoudiniConstant(AssumeCmd assumeCmd)
+ {
+ Variable houdiniConstant;
+ if (houdini.MatchCandidate(assumeCmd.Expr, out houdiniConstant))
+ houdiniAssumeConstants.Add(houdiniConstant);
+ }
+ private Tuple<Variable, Variable> createNewExplainConstants(Variable v)
+ {
+ Contract.Assert(impl != null);
+ Contract.Assert(CommandLineOptions.Clo.ExplainHoudini);
+ Variable v1 = new Constant(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("{0}_{1}_{2}", v.Name, impl.Name, "pos"), Microsoft.Boogie.BasicType.Bool));
+ Variable v2 = new Constant(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("{0}_{1}_{2}", v.Name, impl.Name, "neg"), Microsoft.Boogie.BasicType.Bool));
+
+ return Tuple.Create(v1, v2);
+ }
+ }
+
+
+
+ public class HoudiniSession {
+
+ public class HoudiniStatistics {
+ public double proverTime = 0;
+ public int numProverQueries = 0;
+ public double unsatCoreProverTime = 0;
+ public int numUnsatCoreProverQueries = 0;
+ public int numUnsatCorePrunings = 0;
+ }
+
+ public string descriptiveName;
+ public HoudiniStatistics stats;
+ private VCExpr conjecture;
+ private ProverInterface.ErrorHandler handler;
+ ConditionGeneration.CounterexampleCollector collector;
+ HashSet<Variable> unsatCoreSet;
+ HashSet<Variable> houdiniConstants;
+ public HashSet<Variable> houdiniAssertConstants;
+ private HashSet<Variable> houdiniAssumeConstants;
+
+ // Extra constants created for ExplainHoudini
+ private HashSet<Variable> explainConstantsPositive;
+ private HashSet<Variable> explainConstantsNegative;
+ private Dictionary<string, Tuple<Variable, Variable>> constantToControl;
+
+ public bool InUnsatCore(Variable constant) {
+ if (unsatCoreSet == null)
+ return true;
+ if (unsatCoreSet.Contains(constant))
+ return true;
+ stats.numUnsatCorePrunings++;
+ return false;
+ }
+
+ public HoudiniSession(Houdini houdini, VCGen vcgen, ProverInterface proverInterface, Program program, Implementation impl, HoudiniStatistics stats, int taskID = -1) {
+ this.descriptiveName = impl.Name;
+ this.stats = stats;
+ collector = new ConditionGeneration.CounterexampleCollector();
+ collector.OnProgress("HdnVCGen", 0, 0, 0.0);
+
+ vcgen.ConvertCFG2DAG(impl, taskID: taskID);
+ ModelViewInfo mvInfo;
+ var gotoCmdOrigins = vcgen.PassifyImpl(impl, out mvInfo);
+
+ ExistentialConstantCollector ecollector;
+ ExistentialConstantCollector.CollectHoudiniConstants(houdini, impl, out ecollector);
+ this.houdiniAssertConstants = ecollector.houdiniAssertConstants;
+ this.houdiniAssumeConstants = ecollector.houdiniAssumeConstants;
+ this.explainConstantsNegative = ecollector.explainNegative;
+ this.explainConstantsPositive = ecollector.explainPositive;
+ this.constantToControl = ecollector.constToControl;
+
+ houdiniConstants = new HashSet<Variable>();
+ houdiniConstants.UnionWith(houdiniAssertConstants);
+ houdiniConstants.UnionWith(houdiniAssumeConstants);
+
+ var exprGen = proverInterface.Context.ExprGen;
+ VCExpr controlFlowVariableExpr = CommandLineOptions.Clo.UseLabels ? null : exprGen.Integer(BigNum.ZERO);
+
+ Dictionary<int, Absy> label2absy;
+ conjecture = vcgen.GenerateVC(impl, controlFlowVariableExpr, out label2absy, proverInterface.Context);
+ 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)));
+ conjecture = exprGen.Implies(eqExpr, conjecture);
+ }
+
+ Macro macro = new Macro(Token.NoToken, descriptiveName, new List<Variable>(), new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", Type.Bool), false));
+ proverInterface.DefineMacro(macro, conjecture);
+ conjecture = exprGen.Function(macro);
+
+ if (CommandLineOptions.Clo.vcVariety == CommandLineOptions.VCVariety.Local) {
+ handler = new VCGen.ErrorReporterLocal(gotoCmdOrigins, label2absy, impl.Blocks, vcgen.incarnationOriginMap, collector, mvInfo, proverInterface.Context, program);
+ }
+ else {
+ handler = new VCGen.ErrorReporter(gotoCmdOrigins, label2absy, impl.Blocks, vcgen.incarnationOriginMap, collector, mvInfo, proverInterface.Context, program);
+ }
+
+ }
+
+ private VCExpr BuildAxiom(ProverInterface proverInterface, Dictionary<Variable, bool> currentAssignment) {
+ ProverContext proverContext = proverInterface.Context;
+ Boogie2VCExprTranslator exprTranslator = proverContext.BoogieExprTranslator;
+ VCExpressionGenerator exprGen = proverInterface.VCExprGen;
+
+ VCExpr expr = VCExpressionGenerator.True;
+
+ foreach (KeyValuePair<Variable, bool> kv in currentAssignment) {
+ Variable constant = kv.Key;
+ VCExprVar exprVar = exprTranslator.LookupVariable(constant);
+ if (kv.Value) {
+ expr = exprGen.And(expr, exprVar);
+ }
+ else {
+ expr = exprGen.And(expr, exprGen.Not(exprVar));
+ }
+ }
+
+ if (CommandLineOptions.Clo.ExplainHoudini)
+ {
+ // default values for ExplainHoudini control variables
+ foreach (var constant in explainConstantsNegative.Concat(explainConstantsPositive))
+ {
+ expr = exprGen.And(expr, exprTranslator.LookupVariable(constant));
+ }
+ }
+
+ /*
+ foreach (Variable constant in this.houdiniConstants) {
+ VCExprVar exprVar = exprTranslator.LookupVariable(constant);
+ if (currentAssignment[constant]) {
+ expr = exprGen.And(expr, exprVar);
+ }
+ else {
+ expr = exprGen.And(expr, exprGen.Not(exprVar));
+ }
+ }
+ */
+
+ if(CommandLineOptions.Clo.Trace) {
+ Console.WriteLine("Houdini assignment axiom: " + expr);
+ }
+
+ return expr;
+ }
+
+ public ProverInterface.Outcome Verify(ProverInterface proverInterface, Dictionary<Variable, bool> assignment, out List<Counterexample> errors, int taskID = -1) {
+ collector.examples.Clear();
+
+ if (CommandLineOptions.Clo.Trace) {
+ Console.WriteLine("Verifying " + descriptiveName);
+ }
+ DateTime now = DateTime.UtcNow;
+
+ VCExpr vc = proverInterface.VCExprGen.Implies(BuildAxiom(proverInterface, assignment), conjecture);
+ proverInterface.BeginCheck(descriptiveName, vc, handler);
+ ProverInterface.Outcome proverOutcome = proverInterface.CheckOutcome(handler, taskID: taskID);
+
+ double queryTime = (DateTime.UtcNow - now).TotalSeconds;
+ stats.proverTime += queryTime;
+ stats.numProverQueries++;
+ if (CommandLineOptions.Clo.Trace) {
+ Console.WriteLine("Outcome = " + proverOutcome);
+ Console.WriteLine("Time taken = " + queryTime);
+ }
+
+ errors = collector.examples;
+ return proverOutcome;
+ }
+
+ // MAXSAT
+ public void Explain(ProverInterface proverInterface,
+ Dictionary<Variable, bool> assignment, Variable refutedConstant)
+ {
+ Contract.Assert(CommandLineOptions.Clo.ExplainHoudini);
+
+ collector.examples.Clear();
+
+ // debugging
+ houdiniAssertConstants.Iter(v => System.Diagnostics.Debug.Assert(assignment.ContainsKey(v)));
+ houdiniAssumeConstants.Iter(v => System.Diagnostics.Debug.Assert(assignment.ContainsKey(v)));
+ Contract.Assert(assignment.ContainsKey(refutedConstant));
+ Contract.Assert(houdiniAssertConstants.Contains(refutedConstant));
+
+ var hardAssumptions = new List<VCExpr>();
+ var softAssumptions = new List<VCExpr>();
+
+ Boogie2VCExprTranslator exprTranslator = proverInterface.Context.BoogieExprTranslator;
+ VCExpressionGenerator exprGen = proverInterface.VCExprGen;
+ var controlExpr = VCExpressionGenerator.True;
+
+ foreach (var tup in assignment)
+ {
+ Variable constant = tup.Key;
+ VCExprVar exprVar = exprTranslator.LookupVariable(constant);
+ var val = tup.Value;
+
+ if (houdiniAssumeConstants.Contains(constant))
+ {
+ if (tup.Value)
+ hardAssumptions.Add(exprVar);
+ else
+ // Previously removed assumed candidates are the soft constraints
+ softAssumptions.Add(exprVar);
+ }
+ else if (houdiniAssertConstants.Contains(constant))
+ {
+ if (constant == refutedConstant)
+ hardAssumptions.Add(exprVar);
+ else
+ hardAssumptions.Add(exprGen.Not(exprVar));
+ }
+ else
+ {
+ if (tup.Value)
+ hardAssumptions.Add(exprVar);
+ else
+ hardAssumptions.Add(exprGen.Not(exprVar));
+ }
+
+ // For an asserted condition (c ==> \phi),
+ // ExplainHoudini's extra control constants (c_pos, c_neg) are used as follows:
+ // (true, true): "assert \phi"
+ // (false, _): "assert false"
+ // (true, false): "assert true"
+ if (constant != refutedConstant && constantToControl.ContainsKey(constant.Name))
+ {
+ var posControl = constantToControl[constant.Name].Item1;
+ var negControl = constantToControl[constant.Name].Item2;
+
+ // Handle self-recursion
+ if (houdiniAssertConstants.Contains(constant) && houdiniAssumeConstants.Contains(constant))
+ {
+ // disable this assert
+ controlExpr = exprGen.And(controlExpr, exprGen.And(exprTranslator.LookupVariable(posControl), exprGen.Not(exprTranslator.LookupVariable(negControl))));
+ }
+ else
+ {
+ // default values for control variables
+ controlExpr = exprGen.And(controlExpr, exprGen.And(exprTranslator.LookupVariable(posControl), exprTranslator.LookupVariable(negControl)));
+ }
+ }
+ }
+
+ hardAssumptions.Add(exprGen.Not(conjecture));
+
+ // default values for control variables
+ Contract.Assert(constantToControl.ContainsKey(refutedConstant.Name));
+ var pc = constantToControl[refutedConstant.Name].Item1;
+ var nc = constantToControl[refutedConstant.Name].Item2;
+
+ var controlExprNoop = exprGen.And(controlExpr,
+ exprGen.And(exprTranslator.LookupVariable(pc), exprTranslator.LookupVariable(nc)));
+
+ var controlExprFalse = exprGen.And(controlExpr,
+ exprGen.And(exprGen.Not(exprTranslator.LookupVariable(pc)), exprGen.Not(exprTranslator.LookupVariable(nc))));
+
+ if (CommandLineOptions.Clo.Trace)
+ {
+ Console.WriteLine("Verifying (MaxSat) " + descriptiveName);
+ }
+ DateTime now = DateTime.UtcNow;
+
+ var el = CommandLineOptions.Clo.ProverCCLimit;
+ CommandLineOptions.Clo.ProverCCLimit = 1;
+
+ var outcome = ProverInterface.Outcome.Undetermined;
+
+ do
+ {
+ List<int> unsatisfiedSoftAssumptions;
+
+ hardAssumptions.Add(controlExprNoop);
+ outcome = proverInterface.CheckAssumptions(hardAssumptions, softAssumptions, out unsatisfiedSoftAssumptions, handler);
+ hardAssumptions.RemoveAt(hardAssumptions.Count - 1);
+
+ if (outcome == ProverInterface.Outcome.TimeOut || outcome == ProverInterface.Outcome.OutOfMemory || outcome == ProverInterface.Outcome.Undetermined)
+ break;
+
+ var reason = new HashSet<string>();
+ unsatisfiedSoftAssumptions.Iter(i => reason.Add(softAssumptions[i].ToString()));
+ if (CommandLineOptions.Clo.Trace)
+ {
+ Console.Write("Reason for removal of {0}: ", refutedConstant.Name);
+ reason.Iter(r => Console.Write("{0} ", r));
+ Console.WriteLine();
+ }
+
+ // Get rid of those constants from the "reason" that can even make
+ // "assert false" pass
+
+ hardAssumptions.Add(controlExprFalse);
+ var softAssumptions2 = new List<VCExpr>();
+ for (int i = 0; i < softAssumptions.Count; i++)
+ {
+ if (unsatisfiedSoftAssumptions.Contains(i))
+ {
+ softAssumptions2.Add(softAssumptions[i]);
+ continue;
+ }
+ hardAssumptions.Add(softAssumptions[i]);
+ }
+
+ var unsatisfiedSoftAssumptions2 = new List<int>();
+ outcome = proverInterface.CheckAssumptions(hardAssumptions, softAssumptions2, out unsatisfiedSoftAssumptions2, handler);
+
+ if (outcome == ProverInterface.Outcome.TimeOut || outcome == ProverInterface.Outcome.OutOfMemory || outcome == ProverInterface.Outcome.Undetermined)
+ break;
+
+ unsatisfiedSoftAssumptions2.Iter(i => reason.Remove(softAssumptions2[i].ToString()));
+ var reason1 = new HashSet<string>(); //these are the reasons for inconsistency
+ unsatisfiedSoftAssumptions2.Iter(i => reason1.Add(softAssumptions2[i].ToString()));
+
+ if (CommandLineOptions.Clo.Trace)
+ {
+ Console.Write("Revised reason for removal of {0}: ", refutedConstant.Name);
+ reason.Iter(r => Console.Write("{0} ", r));
+ Console.WriteLine();
+ }
+ foreach (var r in reason)
+ {
+ Houdini.explainHoudiniDottyFile.WriteLine("{0} -> {1} [ label = \"{2}\" color=red ];", refutedConstant.Name, r, descriptiveName);
+ }
+ //also add the removed reasons using dotted edges (requires- x != 0, requires- x == 0 ==> assert x != 0)
+ foreach (var r in reason1)
+ {
+ Houdini.explainHoudiniDottyFile.WriteLine("{0} -> {1} [ label = \"{2}\" color=blue style=dotted ];", refutedConstant.Name, r, descriptiveName);
+ }
+ } while (false);
+
+ if (outcome == ProverInterface.Outcome.TimeOut || outcome == ProverInterface.Outcome.OutOfMemory || outcome == ProverInterface.Outcome.Undetermined)
+ {
+ Houdini.explainHoudiniDottyFile.WriteLine("{0} -> {1} [ label = \"{2}\" color=red ];", refutedConstant.Name, "TimeOut", descriptiveName);
+ }
+
+ CommandLineOptions.Clo.ProverCCLimit = el;
+
+ double queryTime = (DateTime.UtcNow - now).TotalSeconds;
+ stats.proverTime += queryTime;
+ stats.numProverQueries++;
+ if (CommandLineOptions.Clo.Trace)
+ {
+ Console.WriteLine("Time taken = " + queryTime);
+ }
+ }
+
+ public void UpdateUnsatCore(ProverInterface proverInterface, Dictionary<Variable, bool> assignment)
+ {
+ DateTime now = DateTime.UtcNow;
+
+ Boogie2VCExprTranslator exprTranslator = proverInterface.Context.BoogieExprTranslator;
+ proverInterface.Push();
+ proverInterface.Assert(conjecture, false);
+ foreach (var v in assignment.Keys) {
+ if (assignment[v]) continue;
+ proverInterface.Assert(exprTranslator.LookupVariable(v), false);
+ }
+ List<Variable> assumptionVars = new List<Variable>();
+ List<VCExpr> assumptionExprs = new List<VCExpr>();
+ foreach (var v in assignment.Keys) {
+ if (!assignment[v]) continue;
+ assumptionVars.Add(v);
+ assumptionExprs.Add(exprTranslator.LookupVariable(v));
+ }
+ List<int> unsatCore;
+ ProverInterface.Outcome tmp = proverInterface.CheckAssumptions(assumptionExprs, out unsatCore, handler);
+ System.Diagnostics.Debug.Assert(tmp == ProverInterface.Outcome.Valid);
+ unsatCoreSet = new HashSet<Variable>();
+ foreach (int i in unsatCore)
+ unsatCoreSet.Add(assumptionVars[i]);
+ proverInterface.Pop();
+
+ double unsatCoreQueryTime = (DateTime.UtcNow - now).TotalSeconds;
+ stats.unsatCoreProverTime += unsatCoreQueryTime;
+ stats.numUnsatCoreProverQueries++;
+ }
+
+ }
} \ No newline at end of file
diff --git a/Source/Houdini/ConcurrentHoudini.cs b/Source/Houdini/ConcurrentHoudini.cs
index bfc7cbc1..e13fa802 100644
--- a/Source/Houdini/ConcurrentHoudini.cs
+++ b/Source/Houdini/ConcurrentHoudini.cs
@@ -1,104 +1,104 @@
-using System;
-using System.IO;
-using System.Collections.Generic;
-using System.Collections.Concurrent;
-using System.Diagnostics;
-using System.Diagnostics.Contracts;
-using System.Text.RegularExpressions;
-using System.Linq;
-using VC;
-
-namespace Microsoft.Boogie.Houdini
-{
- public class ConcurrentHoudini : Houdini
- {
-
- protected int taskID;
-
- private static ConcurrentDictionary<string, RefutedAnnotation> refutedSharedAnnotations;
-
- public static ConcurrentDictionary<string, RefutedAnnotation> RefutedSharedAnnotations { get { return refutedSharedAnnotations; } }
-
- public ConcurrentHoudini(int taskId, Program program, HoudiniSession.HoudiniStatistics stats, string cexTraceFile = "houdiniCexTrace.txt") {
- Contract.Assert(taskId >= 0);
- this.program = program;
- this.cexTraceFile = cexTraceFile;
- this.taskID = taskId;
- Initialize(program, stats);
- }
-
- static ConcurrentHoudini()
- {
- refutedSharedAnnotations = new ConcurrentDictionary<string, RefutedAnnotation>();
- }
-
- protected override bool ExchangeRefutedAnnotations()
- {
- int count = 0;
-
- if (CommandLineOptions.Clo.DebugConcurrentHoudini)
- Console.WriteLine("# number of shared refuted annotations: " + refutedSharedAnnotations.Count);
-
- foreach (string key in refutedSharedAnnotations.Keys) {
- KeyValuePair<Variable, bool> kv = currentHoudiniState.Assignment.FirstOrDefault(entry => entry.Key.Name.Equals(key) && entry.Value);
-
- if (kv.Key != null) {
- RefutedAnnotation ra = null;
- Implementation refutationSite = null;
-
- foreach (var r in program.Implementations) {
- if (r.Name.Equals(refutedSharedAnnotations[key].RefutationSite.Name)) {
- refutationSite = r;
- break;
- }
- }
- Debug.Assert(refutationSite != null);
-
- if (refutedSharedAnnotations[key].Kind == RefutedAnnotationKind.REQUIRES) {
- Procedure proc = null;
- foreach (var p in program.Procedures) {
- if (p.Name.Equals(refutedSharedAnnotations[key].CalleeProc.Name)) {
- proc = p;
- break;
- }
- }
- Debug.Assert(proc != null);
- ra = RefutedAnnotation.BuildRefutedRequires(kv.Key, proc, refutationSite);
- } else if (refutedSharedAnnotations[key].Kind == RefutedAnnotationKind.ENSURES)
- ra = RefutedAnnotation.BuildRefutedEnsures(kv.Key, refutationSite);
- else if (refutedSharedAnnotations[key].Kind == RefutedAnnotationKind.ASSERT)
- ra = RefutedAnnotation.BuildRefutedAssert(kv.Key, refutationSite);
- Debug.Assert(ra != null);
-
- if (CommandLineOptions.Clo.DebugConcurrentHoudini)
- Console.WriteLine("(+) " + ra.Constant + "," + ra.Kind + "," + ra.CalleeProc + "," + ra.RefutationSite);
-
- AddRelatedToWorkList(ra);
- UpdateAssignment(ra);
- count++;
- }
- }
-
- return count > 0 ? true : false;
- }
-
- protected override void ShareRefutedAnnotation(RefutedAnnotation refutedAnnotation) {
- refutedSharedAnnotations.TryAdd(refutedAnnotation.Constant.Name, refutedAnnotation);
- }
-
- protected override void ApplyRefutedSharedAnnotations() {
- if (refutedSharedAnnotations.Count > 0) {
- foreach (var v in currentHoudiniState.Assignment.Keys.ToList()) {
- if (refutedSharedAnnotations.ContainsKey(v.Name)) {
- currentHoudiniState.Assignment[v] = false;
- }
- }
- }
- }
-
- protected override int GetTaskID() {
- return taskID;
- }
-
- }
-}
+using System;
+using System.IO;
+using System.Collections.Generic;
+using System.Collections.Concurrent;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Text.RegularExpressions;
+using System.Linq;
+using VC;
+
+namespace Microsoft.Boogie.Houdini
+{
+ public class ConcurrentHoudini : Houdini
+ {
+
+ protected int taskID;
+
+ private static ConcurrentDictionary<string, RefutedAnnotation> refutedSharedAnnotations;
+
+ public static ConcurrentDictionary<string, RefutedAnnotation> RefutedSharedAnnotations { get { return refutedSharedAnnotations; } }
+
+ public ConcurrentHoudini(int taskId, Program program, HoudiniSession.HoudiniStatistics stats, string cexTraceFile = "houdiniCexTrace.txt") {
+ Contract.Assert(taskId >= 0);
+ this.program = program;
+ this.cexTraceFile = cexTraceFile;
+ this.taskID = taskId;
+ Initialize(program, stats);
+ }
+
+ static ConcurrentHoudini()
+ {
+ refutedSharedAnnotations = new ConcurrentDictionary<string, RefutedAnnotation>();
+ }
+
+ protected override bool ExchangeRefutedAnnotations()
+ {
+ int count = 0;
+
+ if (CommandLineOptions.Clo.DebugConcurrentHoudini)
+ Console.WriteLine("# number of shared refuted annotations: " + refutedSharedAnnotations.Count);
+
+ foreach (string key in refutedSharedAnnotations.Keys) {
+ KeyValuePair<Variable, bool> kv = currentHoudiniState.Assignment.FirstOrDefault(entry => entry.Key.Name.Equals(key) && entry.Value);
+
+ if (kv.Key != null) {
+ RefutedAnnotation ra = null;
+ Implementation refutationSite = null;
+
+ foreach (var r in program.Implementations) {
+ if (r.Name.Equals(refutedSharedAnnotations[key].RefutationSite.Name)) {
+ refutationSite = r;
+ break;
+ }
+ }
+ Debug.Assert(refutationSite != null);
+
+ if (refutedSharedAnnotations[key].Kind == RefutedAnnotationKind.REQUIRES) {
+ Procedure proc = null;
+ foreach (var p in program.Procedures) {
+ if (p.Name.Equals(refutedSharedAnnotations[key].CalleeProc.Name)) {
+ proc = p;
+ break;
+ }
+ }
+ Debug.Assert(proc != null);
+ ra = RefutedAnnotation.BuildRefutedRequires(kv.Key, proc, refutationSite);
+ } else if (refutedSharedAnnotations[key].Kind == RefutedAnnotationKind.ENSURES)
+ ra = RefutedAnnotation.BuildRefutedEnsures(kv.Key, refutationSite);
+ else if (refutedSharedAnnotations[key].Kind == RefutedAnnotationKind.ASSERT)
+ ra = RefutedAnnotation.BuildRefutedAssert(kv.Key, refutationSite);
+ Debug.Assert(ra != null);
+
+ if (CommandLineOptions.Clo.DebugConcurrentHoudini)
+ Console.WriteLine("(+) " + ra.Constant + "," + ra.Kind + "," + ra.CalleeProc + "," + ra.RefutationSite);
+
+ AddRelatedToWorkList(ra);
+ UpdateAssignment(ra);
+ count++;
+ }
+ }
+
+ return count > 0 ? true : false;
+ }
+
+ protected override void ShareRefutedAnnotation(RefutedAnnotation refutedAnnotation) {
+ refutedSharedAnnotations.TryAdd(refutedAnnotation.Constant.Name, refutedAnnotation);
+ }
+
+ protected override void ApplyRefutedSharedAnnotations() {
+ if (refutedSharedAnnotations.Count > 0) {
+ foreach (var v in currentHoudiniState.Assignment.Keys.ToList()) {
+ if (refutedSharedAnnotations.ContainsKey(v.Name)) {
+ currentHoudiniState.Assignment[v] = false;
+ }
+ }
+ }
+ }
+
+ protected override int GetTaskID() {
+ return taskID;
+ }
+
+ }
+}
diff --git a/Source/Houdini/Houdini.cs b/Source/Houdini/Houdini.cs
index acdb1ff4..4c4ab5aa 100644
--- a/Source/Houdini/Houdini.cs
+++ b/Source/Houdini/Houdini.cs
@@ -1,1330 +1,1330 @@
-//-----------------------------------------------------------------------------
-//
-// Copyright (C) Microsoft Corporation. All Rights Reserved.
-//
-//-----------------------------------------------------------------------------
-using System;
-using System.Diagnostics.Contracts;
-using System.Collections.Generic;
-using Microsoft.Boogie;
-using Microsoft.Boogie.VCExprAST;
-using VC;
-using System.Collections;
-using System.IO;
-using Microsoft.Boogie.GraphUtil;
-using System.Linq;
-using System.Diagnostics;
-
-namespace Microsoft.Boogie.Houdini {
-
- internal class ReadOnlyDictionary<K, V> {
- private Dictionary<K, V> dictionary;
- public ReadOnlyDictionary(Dictionary<K, V> dictionary) {
- this.dictionary = dictionary;
- }
-
- public Dictionary<K, V>.KeyCollection Keys {
- get { return this.dictionary.Keys; }
- }
-
- public bool TryGetValue(K k, out V v) {
- return this.dictionary.TryGetValue(k, out v);
- }
-
- public bool ContainsKey(K k) {
- return this.dictionary.ContainsKey(k);
- }
- }
-
- public abstract class HoudiniObserver {
- public virtual void UpdateStart(Program program, int numConstants) { }
- public virtual void UpdateIteration() { }
- public virtual void UpdateImplementation(Implementation implementation) { }
- public virtual void UpdateAssignment(Dictionary<Variable, bool> assignment) { }
- public virtual void UpdateOutcome(ProverInterface.Outcome outcome) { }
- public virtual void UpdateEnqueue(Implementation implementation) { }
- public virtual void UpdateDequeue() { }
- public virtual void UpdateConstant(string constantName) { }
- public virtual void UpdateEnd(bool isNormalEnd) { }
- public virtual void UpdateFlushStart() { }
- public virtual void UpdateFlushFinish() { }
- public virtual void SeeException(string msg) { }
- }
-
- public class IterationTimer<K> {
- private Dictionary<K, List<double>> times;
-
- public IterationTimer() {
- times = new Dictionary<K, List<double>>();
- }
-
- public void AddTime(K key, double timeMS) {
- List<double> oldList;
- times.TryGetValue(key, out oldList);
- if (oldList == null) {
- oldList = new List<double>();
- }
- else {
- times.Remove(key);
- }
- oldList.Add(timeMS);
- times.Add(key, oldList);
- }
-
- public void PrintTimes(TextWriter wr) {
- wr.WriteLine("Total procedures: {0}", times.Count);
- double total = 0;
- int totalIters = 0;
- foreach (KeyValuePair<K, List<double>> kv in times) {
- int curIter = 0;
- wr.WriteLine("Times for {0}:", kv.Key);
- foreach (double v in kv.Value) {
- wr.WriteLine(" ({0})\t{1}ms", curIter, v);
- total += v;
- curIter++;
- }
- totalIters += curIter;
- }
- total = total / 1000.0;
- wr.WriteLine("Total time: {0} (s)", total);
- wr.WriteLine("Avg: {0} (s/iter)", total / totalIters);
- }
- }
-
- public class HoudiniTimer : HoudiniObserver {
- private DateTime startT;
- private Implementation curImp;
- private IterationTimer<string> times;
- private TextWriter wr;
-
- public HoudiniTimer(TextWriter wr) {
- this.wr = wr;
- times = new IterationTimer<string>();
- }
- public override void UpdateIteration() {
- startT = DateTime.UtcNow;
- }
- public override void UpdateImplementation(Implementation implementation) {
- curImp = implementation;
- }
- public override void UpdateOutcome(ProverInterface.Outcome o) {
- Contract.Assert(curImp != null);
- DateTime endT = DateTime.UtcNow;
- times.AddTime(curImp.Name, (endT - startT).TotalMilliseconds); // assuming names are unique
- }
- public void PrintTimes() {
- wr.WriteLine("-----------------------------------------");
- wr.WriteLine("Times for each iteration for each procedure");
- wr.WriteLine("-----------------------------------------");
- times.PrintTimes(wr);
- }
- }
-
- public class HoudiniTextReporter : HoudiniObserver {
- private TextWriter wr;
- private int currentIteration = -1;
-
- public HoudiniTextReporter(TextWriter wr) {
- this.wr = wr;
- }
- public override void UpdateStart(Program program, int numConstants) {
- wr.WriteLine("Houdini started:" + program.ToString() + " #constants: " + numConstants.ToString());
- currentIteration = -1;
- wr.Flush();
- }
- public override void UpdateIteration() {
- currentIteration++;
- wr.WriteLine("---------------------------------------");
- wr.WriteLine("Houdini iteration #" + currentIteration);
- wr.Flush();
- }
- public override void UpdateImplementation(Implementation implementation) {
- wr.WriteLine("implementation under analysis :" + implementation.Name);
- wr.Flush();
- }
- public override void UpdateAssignment(Dictionary<Variable, bool> assignment) {
- bool firstTime = true;
- wr.Write("assignment under analysis : axiom (");
- foreach (KeyValuePair<Variable, bool> kv in assignment) {
- if (!firstTime) wr.Write(" && "); else firstTime = false;
- string valString; // ugliness to get it lower cased
- if (kv.Value) valString = "true"; else valString = "false";
- wr.Write(kv.Key + " == " + valString);
- }
- wr.WriteLine(");");
- wr.Flush();
- }
- public override void UpdateOutcome(ProverInterface.Outcome outcome) {
- wr.WriteLine("analysis outcome :" + outcome);
- wr.Flush();
- }
- public override void UpdateEnqueue(Implementation implementation) {
- wr.WriteLine("worklist enqueue :" + implementation.Name);
- wr.Flush();
- }
- public override void UpdateDequeue() {
- wr.WriteLine("worklist dequeue");
- wr.Flush();
- }
- public override void UpdateConstant(string constantName) {
- wr.WriteLine("constant disabled : " + constantName);
- wr.Flush();
- }
- public override void UpdateEnd(bool isNormalEnd) {
- wr.WriteLine("Houdini ended: " + (isNormalEnd ? "Normal" : "Abnormal"));
- wr.WriteLine("Number of iterations: " + (this.currentIteration + 1));
- wr.Flush();
- }
- public override void UpdateFlushStart() {
- wr.WriteLine("***************************************");
- wr.WriteLine("Flushing remaining implementations");
- wr.Flush();
- }
- public override void UpdateFlushFinish() {
- wr.WriteLine("***************************************");
- wr.WriteLine("Flushing finished");
- wr.Flush();
- }
- public override void SeeException(string msg) {
- wr.WriteLine("Caught exception: " + msg);
- wr.Flush();
- }
-
- }
-
- public abstract class ObservableHoudini {
- private List<HoudiniObserver> observers = new List<HoudiniObserver>();
-
- public void AddObserver(HoudiniObserver observer) {
- if (!observers.Contains(observer))
- observers.Add(observer);
- }
- private delegate void NotifyDelegate(HoudiniObserver observer);
-
- private void Notify(NotifyDelegate notifyDelegate) {
- foreach (HoudiniObserver observer in observers) {
- notifyDelegate(observer);
- }
- }
- protected void NotifyStart(Program program, int numConstants) {
- NotifyDelegate notifyDelegate = (NotifyDelegate)delegate(HoudiniObserver r) { r.UpdateStart(program, numConstants); };
- Notify(notifyDelegate);
- }
- protected void NotifyIteration() {
- Notify((NotifyDelegate)delegate(HoudiniObserver r) { r.UpdateIteration(); });
- }
- protected void NotifyImplementation(Implementation implementation) {
- Notify((NotifyDelegate)delegate(HoudiniObserver r) { r.UpdateImplementation(implementation); });
- }
- protected void NotifyAssignment(Dictionary<Variable, bool> assignment) {
- Notify((NotifyDelegate)delegate(HoudiniObserver r) { r.UpdateAssignment(assignment); });
- }
- protected void NotifyOutcome(ProverInterface.Outcome outcome) {
- Notify((NotifyDelegate)delegate(HoudiniObserver r) { r.UpdateOutcome(outcome); });
- }
- protected void NotifyEnqueue(Implementation implementation) {
- Notify((NotifyDelegate)delegate(HoudiniObserver r) { r.UpdateEnqueue(implementation); });
- }
- protected void NotifyDequeue() {
- Notify((NotifyDelegate)delegate(HoudiniObserver r) { r.UpdateDequeue(); });
- }
- protected void NotifyConstant(string constantName) {
- Notify((NotifyDelegate)delegate(HoudiniObserver r) { r.UpdateConstant(constantName); });
- }
- protected void NotifyEnd(bool isNormalEnd) {
- Notify((NotifyDelegate)delegate(HoudiniObserver r) { r.UpdateEnd(isNormalEnd); });
- }
- protected void NotifyFlushStart() {
- Notify((NotifyDelegate)delegate(HoudiniObserver r) { r.UpdateFlushStart(); });
- }
- protected void NotifyFlushFinish() {
- Notify((NotifyDelegate)delegate(HoudiniObserver r) { r.UpdateFlushFinish(); });
- }
-
- protected void NotifyException(string msg) {
- Notify((NotifyDelegate)delegate(HoudiniObserver r) { r.SeeException(msg); });
- }
- }
-
- public class InlineEnsuresVisitor : ReadOnlyVisitor {
- public override Ensures VisitEnsures(Ensures ensures)
- {
- if (!ensures.Free)
- {
- ensures.Attributes = new QKeyValue(Token.NoToken, "InlineAssume", new List<object>(), ensures.Attributes);
- }
- return base.VisitEnsures(ensures);
- }
- }
-
- public class Houdini : ObservableHoudini {
- protected Program program;
- protected HashSet<Variable> houdiniConstants;
- protected VCGen vcgen;
- protected ProverInterface proverInterface;
- protected Graph<Implementation> callGraph;
- protected HashSet<Implementation> vcgenFailures;
- protected HoudiniState currentHoudiniState;
- protected CrossDependencies crossDependencies;
- internal ReadOnlyDictionary<Implementation, HoudiniSession> houdiniSessions;
-
- protected string cexTraceFile;
-
- public HoudiniState CurrentHoudiniState { get { return currentHoudiniState; } }
-
- public static TextWriter explainHoudiniDottyFile;
-
- protected Houdini() { }
-
- public Houdini(Program program, HoudiniSession.HoudiniStatistics stats, string cexTraceFile = "houdiniCexTrace.txt") {
- this.program = program;
- this.cexTraceFile = cexTraceFile;
- Initialize(program, stats);
- }
-
- protected void Initialize(Program program, HoudiniSession.HoudiniStatistics stats)
- {
- if (CommandLineOptions.Clo.Trace)
- Console.WriteLine("Collecting existential constants...");
- this.houdiniConstants = CollectExistentialConstants();
-
- if (CommandLineOptions.Clo.Trace)
- Console.WriteLine("Building call graph...");
- this.callGraph = Program.BuildCallGraph(program);
- if (CommandLineOptions.Clo.Trace)
- Console.WriteLine("Number of implementations = {0}", callGraph.Nodes.Count);
-
- if (CommandLineOptions.Clo.HoudiniUseCrossDependencies)
- {
- if (CommandLineOptions.Clo.Trace) Console.WriteLine("Computing procedure cross dependencies ...");
- this.crossDependencies = new CrossDependencies(this.houdiniConstants);
- this.crossDependencies.Visit(program);
- }
-
- Inline();
- /*
- {
- int oldPrintUnstructured = CommandLineOptions.Clo.PrintUnstructured;
- CommandLineOptions.Clo.PrintUnstructured = 1;
- using (TokenTextWriter stream = new TokenTextWriter("houdini_inline.bpl"))
- {
- program.Emit(stream);
- }
- CommandLineOptions.Clo.PrintUnstructured = oldPrintUnstructured;
- }
- */
-
- this.vcgen = new VCGen(program, CommandLineOptions.Clo.SimplifyLogFilePath, CommandLineOptions.Clo.SimplifyLogFileAppend, new List<Checker>());
- this.proverInterface = ProverInterface.CreateProver(program, CommandLineOptions.Clo.SimplifyLogFilePath, CommandLineOptions.Clo.SimplifyLogFileAppend, CommandLineOptions.Clo.ProverKillTime, taskID: GetTaskID());
-
- vcgenFailures = new HashSet<Implementation>();
- Dictionary<Implementation, HoudiniSession> houdiniSessions = new Dictionary<Implementation, HoudiniSession>();
- if (CommandLineOptions.Clo.Trace)
- Console.WriteLine("Beginning VC generation for Houdini...");
- foreach (Implementation impl in callGraph.Nodes)
- {
- try
- {
- if (CommandLineOptions.Clo.Trace)
- Console.WriteLine("Generating VC for {0}", impl.Name);
- HoudiniSession session = new HoudiniSession(this, vcgen, proverInterface, program, impl, stats, taskID: GetTaskID());
- houdiniSessions.Add(impl, session);
- }
- catch (VCGenException)
- {
- if (CommandLineOptions.Clo.Trace)
- Console.WriteLine("VC generation failed");
- vcgenFailures.Add(impl);
- }
- }
- this.houdiniSessions = new ReadOnlyDictionary<Implementation, HoudiniSession>(houdiniSessions);
-
- if (CommandLineOptions.Clo.ExplainHoudini)
- {
- // Print results of ExplainHoudini to a dotty file
- explainHoudiniDottyFile = new StreamWriter("explainHoudini.dot");
- explainHoudiniDottyFile.WriteLine("digraph explainHoudini {");
- foreach (var constant in houdiniConstants)
- explainHoudiniDottyFile.WriteLine("{0} [ label = \"{0}\" color=black ];", constant.Name);
- explainHoudiniDottyFile.WriteLine("TimeOut [label = \"TimeOut\" color=red ];");
- }
- }
-
- protected void Inline() {
- if (CommandLineOptions.Clo.InlineDepth <= 0)
- return;
-
- foreach (Implementation impl in callGraph.Nodes) {
- InlineEnsuresVisitor inlineEnsuresVisitor = new InlineEnsuresVisitor();
- inlineEnsuresVisitor.Visit(impl);
- }
-
- foreach (Implementation impl in callGraph.Nodes) {
- impl.OriginalBlocks = impl.Blocks;
- impl.OriginalLocVars = impl.LocVars;
- }
- foreach (Implementation impl in callGraph.Nodes) {
- CommandLineOptions.Inlining savedOption = CommandLineOptions.Clo.ProcedureInlining;
- CommandLineOptions.Clo.ProcedureInlining = CommandLineOptions.Inlining.Spec;
- Inliner.ProcessImplementationForHoudini(program, impl);
- CommandLineOptions.Clo.ProcedureInlining = savedOption;
- }
- foreach (Implementation impl in callGraph.Nodes) {
- impl.OriginalBlocks = null;
- impl.OriginalLocVars = null;
- }
-
- Graph<Implementation> oldCallGraph = callGraph;
- callGraph = new Graph<Implementation>();
- foreach (Implementation impl in oldCallGraph.Nodes) {
- callGraph.AddSource(impl);
- }
- foreach (Tuple<Implementation, Implementation> edge in oldCallGraph.Edges) {
- callGraph.AddEdge(edge.Item1, edge.Item2);
- }
- int count = CommandLineOptions.Clo.InlineDepth;
- while (count > 0) {
- foreach (Implementation impl in oldCallGraph.Nodes) {
- List<Implementation> newNodes = new List<Implementation>();
- foreach (Implementation succ in callGraph.Successors(impl)) {
- newNodes.AddRange(oldCallGraph.Successors(succ));
- }
- foreach (Implementation newNode in newNodes) {
- callGraph.AddEdge(impl, newNode);
- }
- }
- count--;
- }
- }
-
- protected HashSet<Variable> CollectExistentialConstants() {
- HashSet<Variable> existentialConstants = new HashSet<Variable>();
- foreach (var constant in program.Constants) {
- bool result = false;
- if (constant.CheckBooleanAttribute("existential", ref result)) {
- if (result == true)
- existentialConstants.Add(constant);
- }
- }
- return existentialConstants;
- }
-
- // Compute dependencies between candidates
- public class CrossDependencies : ReadOnlyVisitor
- {
- public CrossDependencies(HashSet<Variable> constants)
- {
- this.constants = constants;
- }
-
- public override Program VisitProgram(Program node)
- {
- assumedInImpl = new Dictionary<string, HashSet<Implementation>>();
- return base.VisitProgram(node);
- }
-
- public override Implementation VisitImplementation(Implementation node)
- {
- curImpl = node;
- return base.VisitImplementation(node);
- }
-
- public override Cmd VisitAssumeCmd(AssumeCmd node)
- {
- return base.VisitAssumeCmd(node);
- }
-
- public override Variable VisitVariable(Variable node)
- {
- if (node is Constant)
- {
- var constant = node as Constant;
- if (constants.Contains(constant))
- {
- if (!assumedInImpl.ContainsKey(constant.Name))
- assumedInImpl[constant.Name] = new HashSet<Implementation>();
- assumedInImpl[constant.Name].Add(curImpl);
- }
- }
- return base.VisitVariable(node);
- }
-
- HashSet<Variable> constants;
- Implementation curImpl;
-
- // contant -> set of implementations that have an assume command with that constant
- public Dictionary<string, HashSet<Implementation>> assumedInImpl { get; private set; }
- }
-
- protected WorkQueue BuildWorkList(Program program) {
- // adding implementations to the workqueue from the bottom of the call graph upwards
- WorkQueue queue = new WorkQueue();
- StronglyConnectedComponents<Implementation> sccs =
- new StronglyConnectedComponents<Implementation>(callGraph.Nodes,
- new Adjacency<Implementation>(callGraph.Predecessors),
- new Adjacency<Implementation>(callGraph.Successors));
- sccs.Compute();
- foreach (SCC<Implementation> scc in sccs) {
- foreach (Implementation impl in scc) {
- if (vcgenFailures.Contains(impl)) continue;
- queue.Enqueue(impl);
- }
- }
- if (CommandLineOptions.Clo.ReverseHoudiniWorklist)
- queue = queue.Reverse();
- return queue;
- /*
- Queue<Implementation> queue = new Queue<Implementation>();
- foreach (Declaration decl in program.TopLevelDeclarations) {
- Implementation impl = decl as Implementation;
- if (impl == null || impl.SkipVerification) continue;
- queue.Enqueue(impl);
- }
- return queue;
- */
- }
-
- public static bool MatchCandidate(Expr boogieExpr, IEnumerable<string> candidates, out string candidateConstant) {
- candidateConstant = null;
- NAryExpr e = boogieExpr as NAryExpr;
- if (e != null && e.Fun is BinaryOperator && ((BinaryOperator)e.Fun).Op == BinaryOperator.Opcode.Imp) {
- Expr antecedent = e.Args[0];
- Expr consequent = e.Args[1];
-
- IdentifierExpr id = antecedent as IdentifierExpr;
- if (id != null && id.Decl is Constant && candidates.Contains(id.Decl.Name)) {
- candidateConstant = id.Decl.Name;
- return true;
- }
-
- if (MatchCandidate(consequent, candidates, out candidateConstant))
- return true;
- }
- return false;
- }
-
- public static bool GetCandidateWithoutConstant(Expr boogieExpr, IEnumerable<string> candidates, out string candidateConstant, out Expr exprWithoutConstant) {
- candidateConstant = null;
- exprWithoutConstant = null;
- NAryExpr e = boogieExpr as NAryExpr;
- if (e != null && e.Fun is BinaryOperator && ((BinaryOperator)e.Fun).Op == BinaryOperator.Opcode.Imp) {
- Expr antecedent = e.Args[0];
- Expr consequent = e.Args[1];
-
- IdentifierExpr id = antecedent as IdentifierExpr;
- if (id != null && id.Decl is Constant && candidates.Contains(id.Decl.Name)) {
- candidateConstant = id.Decl.Name;
- exprWithoutConstant = consequent;
- return true;
- }
-
- if (GetCandidateWithoutConstant(consequent, candidates, out candidateConstant, out exprWithoutConstant))
- {
- exprWithoutConstant = Expr.Imp(antecedent, exprWithoutConstant);
- return true;
- }
- }
- return false;
- }
-
- private static Expr AddConditionToCandidateRec(Expr boogieExpr, Expr condition, string candidateConstant, List<Expr> implicationStack)
- {
- NAryExpr e = boogieExpr as NAryExpr;
- if (e != null && e.Fun is BinaryOperator && ((BinaryOperator)e.Fun).Op == BinaryOperator.Opcode.Imp)
- {
- Expr antecedent = e.Args[0];
- Expr consequent = e.Args[1];
-
- IdentifierExpr id = antecedent as IdentifierExpr;
- if (id != null && id.Decl is Constant && id.Decl.Name.Equals(candidateConstant))
- {
- Expr result = Expr.Imp(antecedent, Expr.Imp(condition, consequent));
- implicationStack.Reverse();
- foreach (var expr in implicationStack)
- {
- result = Expr.Imp(expr, result);
- }
- return result;
- }
-
- implicationStack.Add(antecedent);
- return AddConditionToCandidateRec(consequent, condition, candidateConstant,
- implicationStack);
- }
- return boogieExpr;
- }
-
- public static Expr AddConditionToCandidate(Expr boogieExpr, Expr condition, string candidateConstant)
- {
- return AddConditionToCandidateRec(boogieExpr, condition, candidateConstant, new List<Expr>());
- }
-
- public bool MatchCandidate(Expr boogieExpr, out Variable candidateConstant) {
- candidateConstant = null;
- string candidateString;
- if(MatchCandidate(boogieExpr, houdiniConstants.Select(item => item.Name), out candidateString)) {
- candidateConstant = houdiniConstants.Where(item => item.Name.Equals(candidateString)).ToList()[0];
- return true;
- }
- return false;
- }
-
- public bool MatchCandidate(Expr boogieExpr, out string candidateConstant) {
- return MatchCandidate(boogieExpr, houdiniConstants.Select(item => item.Name), out candidateConstant);
- }
-
- // For Explain houdini: it decorates the condition \phi as (vpos && (\phi || \not vneg))
- // Precondition: MatchCandidate returns true
- public Expr InsertCandidateControl(Expr boogieExpr, Variable vpos, Variable vneg)
- {
- Contract.Assert(CommandLineOptions.Clo.ExplainHoudini);
-
- NAryExpr e = boogieExpr as NAryExpr;
- if (e != null && e.Fun is BinaryOperator && ((BinaryOperator)e.Fun).Op == BinaryOperator.Opcode.Imp)
- {
- Expr antecedent = e.Args[0];
- Expr consequent = e.Args[1];
-
- IdentifierExpr id = antecedent as IdentifierExpr;
- if (id != null && id.Decl is Constant && houdiniConstants.Contains((Constant)id.Decl))
- {
- return Expr.Imp(antecedent, Expr.And(Expr.Ident(vpos), Expr.Or(consequent, Expr.Not(Expr.Ident(vneg)))));
- }
-
- return Expr.Imp(antecedent, InsertCandidateControl(consequent, vpos, vneg));
- }
- Contract.Assert(false);
- return null;
- }
-
- protected Dictionary<Variable, bool> BuildAssignment(HashSet<Variable> constants) {
- Dictionary<Variable, bool> initial = new Dictionary<Variable, bool>();
- foreach (var constant in constants)
- initial.Add(constant, true);
- return initial;
- }
-
- private bool IsOutcomeNotHoudini(ProverInterface.Outcome outcome, List<Counterexample> errors) {
- switch (outcome) {
- case ProverInterface.Outcome.Valid:
- return false;
- case ProverInterface.Outcome.Invalid:
- Contract.Assume(errors != null);
- foreach (Counterexample error in errors) {
- if (ExtractRefutedAnnotation(error) == null)
- return true;
- }
- return false;
- default:
- return true;
- }
- }
-
- // Record most current non-candidate errors found
- // Return true if there was at least one non-candidate error
- protected bool UpdateHoudiniOutcome(HoudiniOutcome houdiniOutcome,
- Implementation implementation,
- ProverInterface.Outcome outcome,
- List<Counterexample> errors) {
- string implName = implementation.Name;
- houdiniOutcome.implementationOutcomes.Remove(implName);
- List<Counterexample> nonCandidateErrors = new List<Counterexample>();
-
- if (outcome == ProverInterface.Outcome.Invalid) {
- foreach (Counterexample error in errors) {
- if (ExtractRefutedAnnotation(error) == null)
- nonCandidateErrors.Add(error);
- }
- }
- houdiniOutcome.implementationOutcomes.Add(implName, new VCGenOutcome(outcome, nonCandidateErrors));
- return nonCandidateErrors.Count > 0;
- }
-
- protected void FlushWorkList(int stage, IEnumerable<int> completedStages) {
- this.NotifyFlushStart();
- while (currentHoudiniState.WorkQueue.Count > 0) {
- this.NotifyIteration();
-
- currentHoudiniState.Implementation = currentHoudiniState.WorkQueue.Peek();
- this.NotifyImplementation(currentHoudiniState.Implementation);
-
- HoudiniSession session;
- houdiniSessions.TryGetValue(currentHoudiniState.Implementation, out session);
- List<Counterexample> errors;
- ProverInterface.Outcome outcome = TryCatchVerify(session, stage, completedStages, out errors);
- UpdateHoudiniOutcome(currentHoudiniState.Outcome, currentHoudiniState.Implementation, outcome, errors);
- this.NotifyOutcome(outcome);
-
- currentHoudiniState.WorkQueue.Dequeue();
- this.NotifyDequeue();
- }
- this.NotifyFlushFinish();
- }
-
- protected void UpdateAssignment(RefutedAnnotation refAnnot) {
- if (CommandLineOptions.Clo.Trace) {
- Console.WriteLine("Removing " + refAnnot.Constant);
- using (var cexWriter = new System.IO.StreamWriter(cexTraceFile, true))
- cexWriter.WriteLine("Removing " + refAnnot.Constant);
- }
- currentHoudiniState.Assignment.Remove(refAnnot.Constant);
- currentHoudiniState.Assignment.Add(refAnnot.Constant, false);
- this.NotifyConstant(refAnnot.Constant.Name);
- }
-
- protected void AddRelatedToWorkList(RefutedAnnotation refutedAnnotation) {
- Contract.Assume(currentHoudiniState.Implementation != null);
- foreach (Implementation implementation in FindImplementationsToEnqueue(refutedAnnotation, refutedAnnotation.RefutationSite)) {
- if (!currentHoudiniState.isBlackListed(implementation.Name)) {
- currentHoudiniState.WorkQueue.Enqueue(implementation);
- this.NotifyEnqueue(implementation);
- }
- }
- }
-
- // Updates the worklist and current assignment
- // @return true if the current function is dequeued
- protected bool UpdateAssignmentWorkList(ProverInterface.Outcome outcome,
- List<Counterexample> errors) {
- Contract.Assume(currentHoudiniState.Implementation != null);
- bool dequeue = true;
-
- switch (outcome) {
- case ProverInterface.Outcome.Valid:
- //yeah, dequeue
- break;
-
- case ProverInterface.Outcome.Invalid:
- Contract.Assume(errors != null);
-
- foreach (Counterexample error in errors) {
- RefutedAnnotation refutedAnnotation = ExtractRefutedAnnotation(error);
- if (refutedAnnotation != null) {
- // some candidate annotation removed
- ShareRefutedAnnotation(refutedAnnotation);
- AddRelatedToWorkList(refutedAnnotation);
- UpdateAssignment(refutedAnnotation);
- dequeue = false;
- #region Extra debugging output
- if (CommandLineOptions.Clo.Trace) {
- using (var cexWriter = new System.IO.StreamWriter(cexTraceFile, true)) {
- cexWriter.WriteLine("Counter example for " + refutedAnnotation.Constant);
- cexWriter.Write(error.ToString());
- cexWriter.WriteLine();
- using (var writer = new Microsoft.Boogie.TokenTextWriter(cexWriter, /*pretty=*/ false))
- foreach (Microsoft.Boogie.Block blk in error.Trace)
- blk.Emit(writer, 15);
- //cexWriter.WriteLine();
- }
- }
- #endregion
- }
- }
-
- if (ExchangeRefutedAnnotations()) dequeue = false;
-
- break;
- default:
- if (CommandLineOptions.Clo.Trace) {
- Console.WriteLine("Timeout/Spaceout while verifying " + currentHoudiniState.Implementation.Name);
- }
- HoudiniSession houdiniSession;
- houdiniSessions.TryGetValue(currentHoudiniState.Implementation, out houdiniSession);
- foreach (Variable v in houdiniSession.houdiniAssertConstants) {
- if (CommandLineOptions.Clo.Trace) {
- Console.WriteLine("Removing " + v);
- }
- currentHoudiniState.Assignment.Remove(v);
- currentHoudiniState.Assignment.Add(v, false);
- this.NotifyConstant(v.Name);
- }
- currentHoudiniState.addToBlackList(currentHoudiniState.Implementation.Name);
- break;
- }
-
- return dequeue;
- }
-
- // This method is a hook used by ConcurrentHoudini to
- // exchange refuted annotations with other Houdini engines.
- // If the method returns true, this indicates that at least
- // one new refutation was received from some other engine.
- // In the base class we thus return false.
- protected virtual bool ExchangeRefutedAnnotations() {
- return false;
- }
-
- // This method is a hook used by ConcurrentHoudini to
- // apply a set of existing refuted annotations at the
- // start of inference.
- protected virtual void ApplyRefutedSharedAnnotations() {
- // Empty in base class; can be overridden.
- }
-
- // This method is a hook used by ConcurrentHoudini to
- // broadcast to other Houdini engines the fact that an
- // annotation was refuted.
- protected virtual void ShareRefutedAnnotation(RefutedAnnotation refutedAnnotation) {
- // Empty in base class; can be overridden.
- }
-
- // Hook for ConcurrentHoudini, which requires a task id.
- // Non-concurrent Houdini has -1 as a task id
- protected virtual int GetTaskID() {
- return -1;
- }
-
- public class WorkQueue {
- private Queue<Implementation> queue;
- private HashSet<Implementation> set;
- public WorkQueue() {
- queue = new Queue<Implementation>();
- set = new HashSet<Implementation>();
- }
- public void Enqueue(Implementation impl) {
- if (set.Contains(impl))
- return;
- queue.Enqueue(impl);
- set.Add(impl);
- }
- public Implementation Dequeue() {
- Implementation impl = queue.Dequeue();
- set.Remove(impl);
- return impl;
- }
- public Implementation Peek() {
- return queue.Peek();
- }
- public int Count {
- get { return queue.Count; }
- }
- public bool Contains(Implementation impl) {
- return set.Contains(impl);
- }
- public WorkQueue Reverse()
- {
- var ret = new WorkQueue();
- foreach (var impl in queue.Reverse())
- ret.Enqueue(impl);
- return ret;
- }
- }
-
- public class HoudiniState {
- public WorkQueue _workQueue;
- public HashSet<string> blackList;
- public Dictionary<Variable, bool> _assignment;
- public Implementation _implementation;
- public HoudiniOutcome _outcome;
-
- public HoudiniState(WorkQueue workQueue, Dictionary<Variable, bool> currentAssignment) {
- this._workQueue = workQueue;
- this._assignment = currentAssignment;
- this._implementation = null;
- this._outcome = new HoudiniOutcome();
- this.blackList = new HashSet<string>();
- }
-
- public WorkQueue WorkQueue {
- get { return this._workQueue; }
- }
- public Dictionary<Variable, bool> Assignment {
- get { return this._assignment; }
- }
- public Implementation Implementation {
- get { return this._implementation; }
- set { this._implementation = value; }
- }
- public HoudiniOutcome Outcome {
- get { return this._outcome; }
- }
- public bool isBlackListed(string funcName) {
- return blackList.Contains(funcName);
- }
- public void addToBlackList(string funcName) {
- blackList.Add(funcName);
- }
- }
-
- public HoudiniOutcome PerformHoudiniInference(int stage = 0,
- IEnumerable<int> completedStages = null,
- Dictionary<string, bool> initialAssignment = null) {
- this.NotifyStart(program, houdiniConstants.Count);
-
- currentHoudiniState = new HoudiniState(BuildWorkList(program), BuildAssignment(houdiniConstants));
-
- if(initialAssignment != null) {
- foreach(var v in CurrentHoudiniState.Assignment.Keys.ToList()) {
- CurrentHoudiniState.Assignment[v] = initialAssignment[v.Name];
- }
- }
-
- ApplyRefutedSharedAnnotations();
-
- foreach (Implementation impl in vcgenFailures) {
- currentHoudiniState.addToBlackList(impl.Name);
- }
-
- while (currentHoudiniState.WorkQueue.Count > 0) {
- this.NotifyIteration();
-
- currentHoudiniState.Implementation = currentHoudiniState.WorkQueue.Peek();
- this.NotifyImplementation(currentHoudiniState.Implementation);
-
- HoudiniSession session;
- this.houdiniSessions.TryGetValue(currentHoudiniState.Implementation, out session);
- HoudiniVerifyCurrent(session, stage, completedStages);
- }
-
- this.NotifyEnd(true);
- Dictionary<string, bool> assignment = new Dictionary<string, bool>();
- foreach (var x in currentHoudiniState.Assignment)
- assignment[x.Key.Name] = x.Value;
- currentHoudiniState.Outcome.assignment = assignment;
- return currentHoudiniState.Outcome;
- }
-
- public void Close() {
- vcgen.Close();
- proverInterface.Close();
- if (CommandLineOptions.Clo.ExplainHoudini)
- {
- explainHoudiniDottyFile.WriteLine("};");
- explainHoudiniDottyFile.Close();
- }
- }
-
- private int NumberOfStages()
- {
- int result = 1;
- foreach(var c in program.Constants) {
- result = Math.Max(result, 1 + QKeyValue.FindIntAttribute(c.Attributes, "stage_active", -1));
- }
- return result;
- }
-
- private List<Implementation> FindImplementationsToEnqueue(RefutedAnnotation refutedAnnotation, Implementation currentImplementation) {
- HoudiniSession session;
- List<Implementation> implementations = new List<Implementation>();
- switch (refutedAnnotation.Kind) {
- case RefutedAnnotationKind.REQUIRES:
- foreach (Implementation callee in callGraph.Successors(currentImplementation))
- {
- if (vcgenFailures.Contains(callee)) continue;
- houdiniSessions.TryGetValue(callee, out session);
- Contract.Assume(callee.Proc != null);
- if (callee.Proc.Equals(refutedAnnotation.CalleeProc) && session.InUnsatCore(refutedAnnotation.Constant))
- implementations.Add(callee);
- }
- break;
- case RefutedAnnotationKind.ENSURES:
- foreach (Implementation caller in callGraph.Predecessors(currentImplementation))
- {
- if (vcgenFailures.Contains(caller)) continue;
- houdiniSessions.TryGetValue(caller, out session);
- if (session.InUnsatCore(refutedAnnotation.Constant))
- implementations.Add(caller);
- }
- break;
- case RefutedAnnotationKind.ASSERT: //the implementation is already in queue
- if (CommandLineOptions.Clo.HoudiniUseCrossDependencies && crossDependencies.assumedInImpl.ContainsKey(refutedAnnotation.Constant.Name))
- {
- foreach (var impl in crossDependencies.assumedInImpl[refutedAnnotation.Constant.Name])
- {
- if (vcgenFailures.Contains(impl)) continue;
- houdiniSessions.TryGetValue(impl, out session);
- if (session.InUnsatCore(refutedAnnotation.Constant))
- implementations.Add(impl);
- }
- }
- break;
- default:
- throw new Exception("Unknown Refuted annotation kind:" + refutedAnnotation.Kind);
- }
- return implementations;
- }
-
- public enum RefutedAnnotationKind { REQUIRES, ENSURES, ASSERT };
-
- public class RefutedAnnotation {
- private Variable _constant;
- private RefutedAnnotationKind _kind;
- private Procedure _callee;
- private Implementation _refutationSite;
-
- private RefutedAnnotation(Variable constant, RefutedAnnotationKind kind, Procedure callee, Implementation refutationSite) {
- this._constant = constant;
- this._kind = kind;
- this._callee = callee;
- this._refutationSite = refutationSite;
- }
- public RefutedAnnotationKind Kind {
- get { return this._kind; }
- }
- public Variable Constant {
- get { return this._constant; }
- }
- public Procedure CalleeProc {
- get { return this._callee; }
- }
- public Implementation RefutationSite {
- get { return this._refutationSite; }
- }
- public static RefutedAnnotation BuildRefutedRequires(Variable constant, Procedure callee, Implementation refutationSite) {
- return new RefutedAnnotation(constant, RefutedAnnotationKind.REQUIRES, callee, refutationSite);
- }
- public static RefutedAnnotation BuildRefutedEnsures(Variable constant, Implementation refutationSite) {
- return new RefutedAnnotation(constant, RefutedAnnotationKind.ENSURES, null, refutationSite);
- }
- public static RefutedAnnotation BuildRefutedAssert(Variable constant, Implementation refutationSite) {
- return new RefutedAnnotation(constant, RefutedAnnotationKind.ASSERT, null, refutationSite);
- }
-
- public override int GetHashCode()
- {
- unchecked {
- int hash = 17;
- hash = hash * 23 + this.Constant.GetHashCode();
- hash = hash * 23 + this.Kind.GetHashCode();
- if (this.CalleeProc != null)
- hash = hash * 23 + this.CalleeProc.GetHashCode();
- hash = hash * 23 + this.RefutationSite.GetHashCode();
- return hash;
- }
- }
-
- public override bool Equals(object obj) {
- bool result = true;
- var other = obj as RefutedAnnotation;
-
- if (other == null) {
- result = false;
- } else {
- result = result && String.Equals(other.Constant, this.Constant);
- result = result && String.Equals(other.Kind, this.Kind);
- if (other.CalleeProc != null && this.CalleeProc != null)
- result = result && String.Equals(other.CalleeProc, this.CalleeProc);
- result = result && String.Equals(other.RefutationSite, this.RefutationSite);
- }
-
- return result;
- }
- }
-
- private void PrintRefutedCall(CallCounterexample err, XmlSink xmlOut) {
- Expr cond = err.FailingRequires.Condition;
- Variable houdiniConst;
- if (MatchCandidate(cond, out houdiniConst)) {
- xmlOut.WriteError("precondition violation", err.FailingCall.tok, err.FailingRequires.tok, err.Trace);
- }
- }
-
- private void PrintRefutedReturn(ReturnCounterexample err, XmlSink xmlOut) {
- Expr cond = err.FailingEnsures.Condition;
- Variable houdiniConst;
- if (MatchCandidate(cond, out houdiniConst)) {
- xmlOut.WriteError("postcondition violation", err.FailingReturn.tok, err.FailingEnsures.tok, err.Trace);
- }
- }
-
- private void PrintRefutedAssert(AssertCounterexample err, XmlSink xmlOut) {
- Expr cond = err.FailingAssert.OrigExpr;
- Variable houdiniConst;
- if (MatchCandidate(cond, out houdiniConst)) {
- xmlOut.WriteError("postcondition violation", err.FailingAssert.tok, err.FailingAssert.tok, err.Trace);
- }
- }
-
- protected void DebugRefutedCandidates(Implementation curFunc, List<Counterexample> errors) {
- XmlSink xmlRefuted = CommandLineOptions.Clo.XmlRefuted;
- if (xmlRefuted != null && errors != null) {
- DateTime start = DateTime.UtcNow;
- xmlRefuted.WriteStartMethod(curFunc.ToString(), start);
-
- foreach (Counterexample error in errors) {
- CallCounterexample ce = error as CallCounterexample;
- if (ce != null) PrintRefutedCall(ce, xmlRefuted);
- ReturnCounterexample re = error as ReturnCounterexample;
- if (re != null) PrintRefutedReturn(re, xmlRefuted);
- AssertCounterexample ae = error as AssertCounterexample;
- if (ae != null) PrintRefutedAssert(ae, xmlRefuted);
- }
-
- DateTime end = DateTime.UtcNow;
- xmlRefuted.WriteEndMethod("errors", end, end.Subtract(start));
- }
- }
-
- private RefutedAnnotation ExtractRefutedAnnotation(Counterexample error) {
- Variable houdiniConstant;
- CallCounterexample callCounterexample = error as CallCounterexample;
- if (callCounterexample != null) {
- Procedure failingProcedure = callCounterexample.FailingCall.Proc;
- Requires failingRequires = callCounterexample.FailingRequires;
- if (MatchCandidate(failingRequires.Condition, out houdiniConstant)) {
- Contract.Assert(houdiniConstant != null);
- return RefutedAnnotation.BuildRefutedRequires(houdiniConstant, failingProcedure, currentHoudiniState.Implementation);
- }
- }
- ReturnCounterexample returnCounterexample = error as ReturnCounterexample;
- if (returnCounterexample != null) {
- Ensures failingEnsures = returnCounterexample.FailingEnsures;
- if (MatchCandidate(failingEnsures.Condition, out houdiniConstant)) {
- Contract.Assert(houdiniConstant != null);
- return RefutedAnnotation.BuildRefutedEnsures(houdiniConstant, currentHoudiniState.Implementation);
- }
- }
- AssertCounterexample assertCounterexample = error as AssertCounterexample;
- if (assertCounterexample != null) {
- AssertCmd failingAssert = assertCounterexample.FailingAssert;
- if (MatchCandidate(failingAssert.OrigExpr, out houdiniConstant)) {
- Contract.Assert(houdiniConstant != null);
- return RefutedAnnotation.BuildRefutedAssert(houdiniConstant, currentHoudiniState.Implementation);
- }
- }
-
- return null;
- }
-
- private ProverInterface.Outcome TryCatchVerify(HoudiniSession session, int stage, IEnumerable<int> completedStages, out List<Counterexample> errors) {
- ProverInterface.Outcome outcome;
- try {
- outcome = session.Verify(proverInterface, GetAssignmentWithStages(stage, completedStages), out errors, taskID: GetTaskID());
- }
- catch (UnexpectedProverOutputException upo) {
- Contract.Assume(upo != null);
- errors = null;
- outcome = ProverInterface.Outcome.Undetermined;
- }
- return outcome;
- }
-
- protected Dictionary<Variable, bool> GetAssignmentWithStages(int currentStage, IEnumerable<int> completedStages)
- {
- Dictionary<Variable, bool> result = new Dictionary<Variable, bool>(currentHoudiniState.Assignment);
- foreach (var c in program.Constants)
- {
- int stageActive = QKeyValue.FindIntAttribute(c.Attributes, "stage_active", -1);
- if (stageActive != -1)
- {
- result[c] = (stageActive == currentStage);
- }
- int stageComplete = QKeyValue.FindIntAttribute(c.Attributes, "stage_complete", -1);
- if (stageComplete != -1)
- {
- result[c] = (completedStages.Contains(stageComplete));
- }
- }
- return result;
- }
-
- private void HoudiniVerifyCurrent(HoudiniSession session, int stage, IEnumerable<int> completedStages) {
- while (true) {
- this.NotifyAssignment(currentHoudiniState.Assignment);
-
- //check the VC with the current assignment
- List<Counterexample> errors;
- ProverInterface.Outcome outcome = TryCatchVerify(session, stage, completedStages, out errors);
- this.NotifyOutcome(outcome);
-
- DebugRefutedCandidates(currentHoudiniState.Implementation, errors);
-
- #region Explain Houdini
- if (CommandLineOptions.Clo.ExplainHoudini && outcome == ProverInterface.Outcome.Invalid)
- {
- Contract.Assume(errors != null);
- // make a copy of this variable
- errors = new List<Counterexample>(errors);
- var refutedAnnotations = new List<RefutedAnnotation>();
- foreach (Counterexample error in errors)
- {
- RefutedAnnotation refutedAnnotation = ExtractRefutedAnnotation(error);
- if (refutedAnnotation == null || refutedAnnotation.Kind == RefutedAnnotationKind.ASSERT) continue;
- refutedAnnotations.Add(refutedAnnotation);
- }
- foreach (var refutedAnnotation in refutedAnnotations)
- {
- session.Explain(proverInterface, currentHoudiniState.Assignment, refutedAnnotation.Constant);
- }
- }
- #endregion
-
- if (UpdateHoudiniOutcome(currentHoudiniState.Outcome, currentHoudiniState.Implementation, outcome, errors)) { // abort
- currentHoudiniState.WorkQueue.Dequeue();
- this.NotifyDequeue();
- FlushWorkList(stage, completedStages);
- return;
- }
- else if (UpdateAssignmentWorkList(outcome, errors)) {
- if (CommandLineOptions.Clo.UseUnsatCoreForContractInfer && outcome == ProverInterface.Outcome.Valid)
- session.UpdateUnsatCore(proverInterface, currentHoudiniState.Assignment);
- currentHoudiniState.WorkQueue.Dequeue();
- this.NotifyDequeue();
- return;
- }
- }
- }
-
- /// <summary>
- /// Transforms given program based on Houdini outcome. If a constant is assigned "true",
- /// any preconditions or postconditions guarded by the constant are made free, and any assertions
- /// guarded by the constant are replaced with assumptions.
- ///
- /// If a constant is assigned "false", any preconditions or postconditions
- /// guarded by the constant are replaced with "true", and assertions guarded by the constant
- /// are removed.
- ///
- /// In addition, all Houdini constants are removed from the program.
- /// </summary>
- public static void ApplyAssignment(Program prog, HoudiniOutcome outcome) {
-
- var Candidates = prog.TopLevelDeclarations.OfType<Constant>().Where
- (Item => QKeyValue.FindBoolAttribute(Item.Attributes, "existential")).Select(Item => Item.Name);
-
- // Treat all assertions
- // TODO: do we need to also consider assumptions?
- foreach (Block block in prog.Implementations.Select(item => item.Blocks).SelectMany(item => item)) {
- List<Cmd> newCmds = new List<Cmd>();
- foreach (Cmd cmd in block.Cmds) {
- string c;
- AssertCmd assertCmd = cmd as AssertCmd;
- if (assertCmd != null && MatchCandidate(assertCmd.Expr, Candidates, out c)) {
- var cVar = outcome.assignment.Keys.Where(item => item.Equals(c)).ToList()[0];
- if (outcome.assignment[cVar]) {
- Dictionary<Variable, Expr> cToTrue = new Dictionary<Variable, Expr>();
- Variable cVarProg = prog.Variables.Where(item => item.Name.Equals(c)).ToList()[0];
- cToTrue[cVarProg] = Expr.True;
- newCmds.Add(new AssumeCmd(assertCmd.tok,
- Substituter.Apply(Substituter.SubstitutionFromHashtable(cToTrue), assertCmd.Expr),
- assertCmd.Attributes));
- }
- }
- else {
- newCmds.Add(cmd);
- }
- }
- block.Cmds = newCmds;
- }
-
- foreach (var proc in prog.Procedures) {
- List<Requires> newRequires = new List<Requires>();
- foreach (Requires r in proc.Requires) {
- string c;
- if (MatchCandidate(r.Condition, Candidates, out c)) {
- var cVar = outcome.assignment.Keys.Where(item => item.Equals(c)).ToList()[0];
- if (outcome.assignment[cVar]) {
- Variable cVarProg = prog.Variables.Where(item => item.Name.Equals(c)).ToList()[0];
- Dictionary<Variable, Expr> subst = new Dictionary<Variable, Expr>();
- subst[cVarProg] = Expr.True;
- newRequires.Add(new Requires(Token.NoToken, true,
- Substituter.Apply(Substituter.SubstitutionFromHashtable(subst), r.Condition),
- r.Comment, r.Attributes));
- }
- }
- else {
- newRequires.Add(r);
- }
- }
- proc.Requires = newRequires;
-
- List<Ensures> newEnsures = new List<Ensures>();
- foreach (Ensures e in proc.Ensures) {
- string c;
- if (MatchCandidate(e.Condition, Candidates, out c)) {
- var cVar = outcome.assignment.Keys.Where(item => item.Equals(c)).ToList()[0];
- if (outcome.assignment[cVar]) {
- Variable cVarProg = prog.Variables.Where(item => item.Name.Equals(c)).ToList()[0];
- Dictionary<Variable, Expr> subst = new Dictionary<Variable, Expr>();
- subst[cVarProg] = Expr.True;
- newEnsures.Add(new Ensures(Token.NoToken, true,
- Substituter.Apply(Substituter.SubstitutionFromHashtable(subst), e.Condition),
- e.Comment, e.Attributes));
- }
- }
- else {
- newEnsures.Add(e);
- }
- }
- proc.Ensures = newEnsures;
- }
-
- // Remove the existential constants
- prog.RemoveTopLevelDeclarations(item => (item is Constant) &&
- (Candidates.Any(item2 => item2.Equals((item as Constant).Name))));
- }
-
- }
-
- public class VCGenOutcome {
- public VCGen.Outcome outcome;
- public List<Counterexample> errors;
- public VCGenOutcome(ProverInterface.Outcome outcome, List<Counterexample> errors) {
- this.outcome = ConditionGeneration.ProverInterfaceOutcomeToConditionGenerationOutcome(outcome);
- this.errors = errors;
- }
- }
-
- public class HoudiniOutcome {
- // final assignment
- public Dictionary<string, bool> assignment = new Dictionary<string, bool>();
- // boogie errors
- public Dictionary<string, VCGenOutcome> implementationOutcomes = new Dictionary<string, VCGenOutcome>();
-
- // statistics
-
- private int CountResults(VCGen.Outcome outcome) {
- int outcomeCount = 0;
- foreach (VCGenOutcome verifyOutcome in implementationOutcomes.Values) {
- if (verifyOutcome.outcome == outcome)
- outcomeCount++;
- }
- return outcomeCount;
- }
-
- private List<string> ListOutcomeMatches(VCGen.Outcome outcome) {
- List<string> result = new List<string>();
- foreach (KeyValuePair<string, VCGenOutcome> kvpair in implementationOutcomes) {
- if (kvpair.Value.outcome == outcome)
- result.Add(kvpair.Key);
- }
- return result;
- }
-
- public int ErrorCount {
- get {
- return CountResults(VCGen.Outcome.Errors);
- }
- }
- public int Verified {
- get {
- return CountResults(VCGen.Outcome.Correct);
- }
- }
- public int Inconclusives {
- get {
- return CountResults(VCGen.Outcome.Inconclusive);
- }
- }
- public int TimeOuts {
- get {
- return CountResults(VCGen.Outcome.TimedOut);
- }
- }
- public List<string> ListOfTimeouts {
- get {
- return ListOutcomeMatches(VCGen.Outcome.TimedOut);
- }
- }
- public List<string> ListOfInconclusives {
- get {
- return ListOutcomeMatches(VCGen.Outcome.Inconclusive);
- }
- }
- public List<string> ListOfErrors {
- get {
- return ListOutcomeMatches(VCGen.Outcome.Errors);
- }
- }
- }
-
-}
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Diagnostics.Contracts;
+using System.Collections.Generic;
+using Microsoft.Boogie;
+using Microsoft.Boogie.VCExprAST;
+using VC;
+using System.Collections;
+using System.IO;
+using Microsoft.Boogie.GraphUtil;
+using System.Linq;
+using System.Diagnostics;
+
+namespace Microsoft.Boogie.Houdini {
+
+ internal class ReadOnlyDictionary<K, V> {
+ private Dictionary<K, V> dictionary;
+ public ReadOnlyDictionary(Dictionary<K, V> dictionary) {
+ this.dictionary = dictionary;
+ }
+
+ public Dictionary<K, V>.KeyCollection Keys {
+ get { return this.dictionary.Keys; }
+ }
+
+ public bool TryGetValue(K k, out V v) {
+ return this.dictionary.TryGetValue(k, out v);
+ }
+
+ public bool ContainsKey(K k) {
+ return this.dictionary.ContainsKey(k);
+ }
+ }
+
+ public abstract class HoudiniObserver {
+ public virtual void UpdateStart(Program program, int numConstants) { }
+ public virtual void UpdateIteration() { }
+ public virtual void UpdateImplementation(Implementation implementation) { }
+ public virtual void UpdateAssignment(Dictionary<Variable, bool> assignment) { }
+ public virtual void UpdateOutcome(ProverInterface.Outcome outcome) { }
+ public virtual void UpdateEnqueue(Implementation implementation) { }
+ public virtual void UpdateDequeue() { }
+ public virtual void UpdateConstant(string constantName) { }
+ public virtual void UpdateEnd(bool isNormalEnd) { }
+ public virtual void UpdateFlushStart() { }
+ public virtual void UpdateFlushFinish() { }
+ public virtual void SeeException(string msg) { }
+ }
+
+ public class IterationTimer<K> {
+ private Dictionary<K, List<double>> times;
+
+ public IterationTimer() {
+ times = new Dictionary<K, List<double>>();
+ }
+
+ public void AddTime(K key, double timeMS) {
+ List<double> oldList;
+ times.TryGetValue(key, out oldList);
+ if (oldList == null) {
+ oldList = new List<double>();
+ }
+ else {
+ times.Remove(key);
+ }
+ oldList.Add(timeMS);
+ times.Add(key, oldList);
+ }
+
+ public void PrintTimes(TextWriter wr) {
+ wr.WriteLine("Total procedures: {0}", times.Count);
+ double total = 0;
+ int totalIters = 0;
+ foreach (KeyValuePair<K, List<double>> kv in times) {
+ int curIter = 0;
+ wr.WriteLine("Times for {0}:", kv.Key);
+ foreach (double v in kv.Value) {
+ wr.WriteLine(" ({0})\t{1}ms", curIter, v);
+ total += v;
+ curIter++;
+ }
+ totalIters += curIter;
+ }
+ total = total / 1000.0;
+ wr.WriteLine("Total time: {0} (s)", total);
+ wr.WriteLine("Avg: {0} (s/iter)", total / totalIters);
+ }
+ }
+
+ public class HoudiniTimer : HoudiniObserver {
+ private DateTime startT;
+ private Implementation curImp;
+ private IterationTimer<string> times;
+ private TextWriter wr;
+
+ public HoudiniTimer(TextWriter wr) {
+ this.wr = wr;
+ times = new IterationTimer<string>();
+ }
+ public override void UpdateIteration() {
+ startT = DateTime.UtcNow;
+ }
+ public override void UpdateImplementation(Implementation implementation) {
+ curImp = implementation;
+ }
+ public override void UpdateOutcome(ProverInterface.Outcome o) {
+ Contract.Assert(curImp != null);
+ DateTime endT = DateTime.UtcNow;
+ times.AddTime(curImp.Name, (endT - startT).TotalMilliseconds); // assuming names are unique
+ }
+ public void PrintTimes() {
+ wr.WriteLine("-----------------------------------------");
+ wr.WriteLine("Times for each iteration for each procedure");
+ wr.WriteLine("-----------------------------------------");
+ times.PrintTimes(wr);
+ }
+ }
+
+ public class HoudiniTextReporter : HoudiniObserver {
+ private TextWriter wr;
+ private int currentIteration = -1;
+
+ public HoudiniTextReporter(TextWriter wr) {
+ this.wr = wr;
+ }
+ public override void UpdateStart(Program program, int numConstants) {
+ wr.WriteLine("Houdini started:" + program.ToString() + " #constants: " + numConstants.ToString());
+ currentIteration = -1;
+ wr.Flush();
+ }
+ public override void UpdateIteration() {
+ currentIteration++;
+ wr.WriteLine("---------------------------------------");
+ wr.WriteLine("Houdini iteration #" + currentIteration);
+ wr.Flush();
+ }
+ public override void UpdateImplementation(Implementation implementation) {
+ wr.WriteLine("implementation under analysis :" + implementation.Name);
+ wr.Flush();
+ }
+ public override void UpdateAssignment(Dictionary<Variable, bool> assignment) {
+ bool firstTime = true;
+ wr.Write("assignment under analysis : axiom (");
+ foreach (KeyValuePair<Variable, bool> kv in assignment) {
+ if (!firstTime) wr.Write(" && "); else firstTime = false;
+ string valString; // ugliness to get it lower cased
+ if (kv.Value) valString = "true"; else valString = "false";
+ wr.Write(kv.Key + " == " + valString);
+ }
+ wr.WriteLine(");");
+ wr.Flush();
+ }
+ public override void UpdateOutcome(ProverInterface.Outcome outcome) {
+ wr.WriteLine("analysis outcome :" + outcome);
+ wr.Flush();
+ }
+ public override void UpdateEnqueue(Implementation implementation) {
+ wr.WriteLine("worklist enqueue :" + implementation.Name);
+ wr.Flush();
+ }
+ public override void UpdateDequeue() {
+ wr.WriteLine("worklist dequeue");
+ wr.Flush();
+ }
+ public override void UpdateConstant(string constantName) {
+ wr.WriteLine("constant disabled : " + constantName);
+ wr.Flush();
+ }
+ public override void UpdateEnd(bool isNormalEnd) {
+ wr.WriteLine("Houdini ended: " + (isNormalEnd ? "Normal" : "Abnormal"));
+ wr.WriteLine("Number of iterations: " + (this.currentIteration + 1));
+ wr.Flush();
+ }
+ public override void UpdateFlushStart() {
+ wr.WriteLine("***************************************");
+ wr.WriteLine("Flushing remaining implementations");
+ wr.Flush();
+ }
+ public override void UpdateFlushFinish() {
+ wr.WriteLine("***************************************");
+ wr.WriteLine("Flushing finished");
+ wr.Flush();
+ }
+ public override void SeeException(string msg) {
+ wr.WriteLine("Caught exception: " + msg);
+ wr.Flush();
+ }
+
+ }
+
+ public abstract class ObservableHoudini {
+ private List<HoudiniObserver> observers = new List<HoudiniObserver>();
+
+ public void AddObserver(HoudiniObserver observer) {
+ if (!observers.Contains(observer))
+ observers.Add(observer);
+ }
+ private delegate void NotifyDelegate(HoudiniObserver observer);
+
+ private void Notify(NotifyDelegate notifyDelegate) {
+ foreach (HoudiniObserver observer in observers) {
+ notifyDelegate(observer);
+ }
+ }
+ protected void NotifyStart(Program program, int numConstants) {
+ NotifyDelegate notifyDelegate = (NotifyDelegate)delegate(HoudiniObserver r) { r.UpdateStart(program, numConstants); };
+ Notify(notifyDelegate);
+ }
+ protected void NotifyIteration() {
+ Notify((NotifyDelegate)delegate(HoudiniObserver r) { r.UpdateIteration(); });
+ }
+ protected void NotifyImplementation(Implementation implementation) {
+ Notify((NotifyDelegate)delegate(HoudiniObserver r) { r.UpdateImplementation(implementation); });
+ }
+ protected void NotifyAssignment(Dictionary<Variable, bool> assignment) {
+ Notify((NotifyDelegate)delegate(HoudiniObserver r) { r.UpdateAssignment(assignment); });
+ }
+ protected void NotifyOutcome(ProverInterface.Outcome outcome) {
+ Notify((NotifyDelegate)delegate(HoudiniObserver r) { r.UpdateOutcome(outcome); });
+ }
+ protected void NotifyEnqueue(Implementation implementation) {
+ Notify((NotifyDelegate)delegate(HoudiniObserver r) { r.UpdateEnqueue(implementation); });
+ }
+ protected void NotifyDequeue() {
+ Notify((NotifyDelegate)delegate(HoudiniObserver r) { r.UpdateDequeue(); });
+ }
+ protected void NotifyConstant(string constantName) {
+ Notify((NotifyDelegate)delegate(HoudiniObserver r) { r.UpdateConstant(constantName); });
+ }
+ protected void NotifyEnd(bool isNormalEnd) {
+ Notify((NotifyDelegate)delegate(HoudiniObserver r) { r.UpdateEnd(isNormalEnd); });
+ }
+ protected void NotifyFlushStart() {
+ Notify((NotifyDelegate)delegate(HoudiniObserver r) { r.UpdateFlushStart(); });
+ }
+ protected void NotifyFlushFinish() {
+ Notify((NotifyDelegate)delegate(HoudiniObserver r) { r.UpdateFlushFinish(); });
+ }
+
+ protected void NotifyException(string msg) {
+ Notify((NotifyDelegate)delegate(HoudiniObserver r) { r.SeeException(msg); });
+ }
+ }
+
+ public class InlineEnsuresVisitor : ReadOnlyVisitor {
+ public override Ensures VisitEnsures(Ensures ensures)
+ {
+ if (!ensures.Free)
+ {
+ ensures.Attributes = new QKeyValue(Token.NoToken, "InlineAssume", new List<object>(), ensures.Attributes);
+ }
+ return base.VisitEnsures(ensures);
+ }
+ }
+
+ public class Houdini : ObservableHoudini {
+ protected Program program;
+ protected HashSet<Variable> houdiniConstants;
+ protected VCGen vcgen;
+ protected ProverInterface proverInterface;
+ protected Graph<Implementation> callGraph;
+ protected HashSet<Implementation> vcgenFailures;
+ protected HoudiniState currentHoudiniState;
+ protected CrossDependencies crossDependencies;
+ internal ReadOnlyDictionary<Implementation, HoudiniSession> houdiniSessions;
+
+ protected string cexTraceFile;
+
+ public HoudiniState CurrentHoudiniState { get { return currentHoudiniState; } }
+
+ public static TextWriter explainHoudiniDottyFile;
+
+ protected Houdini() { }
+
+ public Houdini(Program program, HoudiniSession.HoudiniStatistics stats, string cexTraceFile = "houdiniCexTrace.txt") {
+ this.program = program;
+ this.cexTraceFile = cexTraceFile;
+ Initialize(program, stats);
+ }
+
+ protected void Initialize(Program program, HoudiniSession.HoudiniStatistics stats)
+ {
+ if (CommandLineOptions.Clo.Trace)
+ Console.WriteLine("Collecting existential constants...");
+ this.houdiniConstants = CollectExistentialConstants();
+
+ if (CommandLineOptions.Clo.Trace)
+ Console.WriteLine("Building call graph...");
+ this.callGraph = Program.BuildCallGraph(program);
+ if (CommandLineOptions.Clo.Trace)
+ Console.WriteLine("Number of implementations = {0}", callGraph.Nodes.Count);
+
+ if (CommandLineOptions.Clo.HoudiniUseCrossDependencies)
+ {
+ if (CommandLineOptions.Clo.Trace) Console.WriteLine("Computing procedure cross dependencies ...");
+ this.crossDependencies = new CrossDependencies(this.houdiniConstants);
+ this.crossDependencies.Visit(program);
+ }
+
+ Inline();
+ /*
+ {
+ int oldPrintUnstructured = CommandLineOptions.Clo.PrintUnstructured;
+ CommandLineOptions.Clo.PrintUnstructured = 1;
+ using (TokenTextWriter stream = new TokenTextWriter("houdini_inline.bpl"))
+ {
+ program.Emit(stream);
+ }
+ CommandLineOptions.Clo.PrintUnstructured = oldPrintUnstructured;
+ }
+ */
+
+ this.vcgen = new VCGen(program, CommandLineOptions.Clo.SimplifyLogFilePath, CommandLineOptions.Clo.SimplifyLogFileAppend, new List<Checker>());
+ this.proverInterface = ProverInterface.CreateProver(program, CommandLineOptions.Clo.SimplifyLogFilePath, CommandLineOptions.Clo.SimplifyLogFileAppend, CommandLineOptions.Clo.ProverKillTime, taskID: GetTaskID());
+
+ vcgenFailures = new HashSet<Implementation>();
+ Dictionary<Implementation, HoudiniSession> houdiniSessions = new Dictionary<Implementation, HoudiniSession>();
+ if (CommandLineOptions.Clo.Trace)
+ Console.WriteLine("Beginning VC generation for Houdini...");
+ foreach (Implementation impl in callGraph.Nodes)
+ {
+ try
+ {
+ if (CommandLineOptions.Clo.Trace)
+ Console.WriteLine("Generating VC for {0}", impl.Name);
+ HoudiniSession session = new HoudiniSession(this, vcgen, proverInterface, program, impl, stats, taskID: GetTaskID());
+ houdiniSessions.Add(impl, session);
+ }
+ catch (VCGenException)
+ {
+ if (CommandLineOptions.Clo.Trace)
+ Console.WriteLine("VC generation failed");
+ vcgenFailures.Add(impl);
+ }
+ }
+ this.houdiniSessions = new ReadOnlyDictionary<Implementation, HoudiniSession>(houdiniSessions);
+
+ if (CommandLineOptions.Clo.ExplainHoudini)
+ {
+ // Print results of ExplainHoudini to a dotty file
+ explainHoudiniDottyFile = new StreamWriter("explainHoudini.dot");
+ explainHoudiniDottyFile.WriteLine("digraph explainHoudini {");
+ foreach (var constant in houdiniConstants)
+ explainHoudiniDottyFile.WriteLine("{0} [ label = \"{0}\" color=black ];", constant.Name);
+ explainHoudiniDottyFile.WriteLine("TimeOut [label = \"TimeOut\" color=red ];");
+ }
+ }
+
+ protected void Inline() {
+ if (CommandLineOptions.Clo.InlineDepth <= 0)
+ return;
+
+ foreach (Implementation impl in callGraph.Nodes) {
+ InlineEnsuresVisitor inlineEnsuresVisitor = new InlineEnsuresVisitor();
+ inlineEnsuresVisitor.Visit(impl);
+ }
+
+ foreach (Implementation impl in callGraph.Nodes) {
+ impl.OriginalBlocks = impl.Blocks;
+ impl.OriginalLocVars = impl.LocVars;
+ }
+ foreach (Implementation impl in callGraph.Nodes) {
+ CommandLineOptions.Inlining savedOption = CommandLineOptions.Clo.ProcedureInlining;
+ CommandLineOptions.Clo.ProcedureInlining = CommandLineOptions.Inlining.Spec;
+ Inliner.ProcessImplementationForHoudini(program, impl);
+ CommandLineOptions.Clo.ProcedureInlining = savedOption;
+ }
+ foreach (Implementation impl in callGraph.Nodes) {
+ impl.OriginalBlocks = null;
+ impl.OriginalLocVars = null;
+ }
+
+ Graph<Implementation> oldCallGraph = callGraph;
+ callGraph = new Graph<Implementation>();
+ foreach (Implementation impl in oldCallGraph.Nodes) {
+ callGraph.AddSource(impl);
+ }
+ foreach (Tuple<Implementation, Implementation> edge in oldCallGraph.Edges) {
+ callGraph.AddEdge(edge.Item1, edge.Item2);
+ }
+ int count = CommandLineOptions.Clo.InlineDepth;
+ while (count > 0) {
+ foreach (Implementation impl in oldCallGraph.Nodes) {
+ List<Implementation> newNodes = new List<Implementation>();
+ foreach (Implementation succ in callGraph.Successors(impl)) {
+ newNodes.AddRange(oldCallGraph.Successors(succ));
+ }
+ foreach (Implementation newNode in newNodes) {
+ callGraph.AddEdge(impl, newNode);
+ }
+ }
+ count--;
+ }
+ }
+
+ protected HashSet<Variable> CollectExistentialConstants() {
+ HashSet<Variable> existentialConstants = new HashSet<Variable>();
+ foreach (var constant in program.Constants) {
+ bool result = false;
+ if (constant.CheckBooleanAttribute("existential", ref result)) {
+ if (result == true)
+ existentialConstants.Add(constant);
+ }
+ }
+ return existentialConstants;
+ }
+
+ // Compute dependencies between candidates
+ public class CrossDependencies : ReadOnlyVisitor
+ {
+ public CrossDependencies(HashSet<Variable> constants)
+ {
+ this.constants = constants;
+ }
+
+ public override Program VisitProgram(Program node)
+ {
+ assumedInImpl = new Dictionary<string, HashSet<Implementation>>();
+ return base.VisitProgram(node);
+ }
+
+ public override Implementation VisitImplementation(Implementation node)
+ {
+ curImpl = node;
+ return base.VisitImplementation(node);
+ }
+
+ public override Cmd VisitAssumeCmd(AssumeCmd node)
+ {
+ return base.VisitAssumeCmd(node);
+ }
+
+ public override Variable VisitVariable(Variable node)
+ {
+ if (node is Constant)
+ {
+ var constant = node as Constant;
+ if (constants.Contains(constant))
+ {
+ if (!assumedInImpl.ContainsKey(constant.Name))
+ assumedInImpl[constant.Name] = new HashSet<Implementation>();
+ assumedInImpl[constant.Name].Add(curImpl);
+ }
+ }
+ return base.VisitVariable(node);
+ }
+
+ HashSet<Variable> constants;
+ Implementation curImpl;
+
+ // contant -> set of implementations that have an assume command with that constant
+ public Dictionary<string, HashSet<Implementation>> assumedInImpl { get; private set; }
+ }
+
+ protected WorkQueue BuildWorkList(Program program) {
+ // adding implementations to the workqueue from the bottom of the call graph upwards
+ WorkQueue queue = new WorkQueue();
+ StronglyConnectedComponents<Implementation> sccs =
+ new StronglyConnectedComponents<Implementation>(callGraph.Nodes,
+ new Adjacency<Implementation>(callGraph.Predecessors),
+ new Adjacency<Implementation>(callGraph.Successors));
+ sccs.Compute();
+ foreach (SCC<Implementation> scc in sccs) {
+ foreach (Implementation impl in scc) {
+ if (vcgenFailures.Contains(impl)) continue;
+ queue.Enqueue(impl);
+ }
+ }
+ if (CommandLineOptions.Clo.ReverseHoudiniWorklist)
+ queue = queue.Reverse();
+ return queue;
+ /*
+ Queue<Implementation> queue = new Queue<Implementation>();
+ foreach (Declaration decl in program.TopLevelDeclarations) {
+ Implementation impl = decl as Implementation;
+ if (impl == null || impl.SkipVerification) continue;
+ queue.Enqueue(impl);
+ }
+ return queue;
+ */
+ }
+
+ public static bool MatchCandidate(Expr boogieExpr, IEnumerable<string> candidates, out string candidateConstant) {
+ candidateConstant = null;
+ NAryExpr e = boogieExpr as NAryExpr;
+ if (e != null && e.Fun is BinaryOperator && ((BinaryOperator)e.Fun).Op == BinaryOperator.Opcode.Imp) {
+ Expr antecedent = e.Args[0];
+ Expr consequent = e.Args[1];
+
+ IdentifierExpr id = antecedent as IdentifierExpr;
+ if (id != null && id.Decl is Constant && candidates.Contains(id.Decl.Name)) {
+ candidateConstant = id.Decl.Name;
+ return true;
+ }
+
+ if (MatchCandidate(consequent, candidates, out candidateConstant))
+ return true;
+ }
+ return false;
+ }
+
+ public static bool GetCandidateWithoutConstant(Expr boogieExpr, IEnumerable<string> candidates, out string candidateConstant, out Expr exprWithoutConstant) {
+ candidateConstant = null;
+ exprWithoutConstant = null;
+ NAryExpr e = boogieExpr as NAryExpr;
+ if (e != null && e.Fun is BinaryOperator && ((BinaryOperator)e.Fun).Op == BinaryOperator.Opcode.Imp) {
+ Expr antecedent = e.Args[0];
+ Expr consequent = e.Args[1];
+
+ IdentifierExpr id = antecedent as IdentifierExpr;
+ if (id != null && id.Decl is Constant && candidates.Contains(id.Decl.Name)) {
+ candidateConstant = id.Decl.Name;
+ exprWithoutConstant = consequent;
+ return true;
+ }
+
+ if (GetCandidateWithoutConstant(consequent, candidates, out candidateConstant, out exprWithoutConstant))
+ {
+ exprWithoutConstant = Expr.Imp(antecedent, exprWithoutConstant);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static Expr AddConditionToCandidateRec(Expr boogieExpr, Expr condition, string candidateConstant, List<Expr> implicationStack)
+ {
+ NAryExpr e = boogieExpr as NAryExpr;
+ if (e != null && e.Fun is BinaryOperator && ((BinaryOperator)e.Fun).Op == BinaryOperator.Opcode.Imp)
+ {
+ Expr antecedent = e.Args[0];
+ Expr consequent = e.Args[1];
+
+ IdentifierExpr id = antecedent as IdentifierExpr;
+ if (id != null && id.Decl is Constant && id.Decl.Name.Equals(candidateConstant))
+ {
+ Expr result = Expr.Imp(antecedent, Expr.Imp(condition, consequent));
+ implicationStack.Reverse();
+ foreach (var expr in implicationStack)
+ {
+ result = Expr.Imp(expr, result);
+ }
+ return result;
+ }
+
+ implicationStack.Add(antecedent);
+ return AddConditionToCandidateRec(consequent, condition, candidateConstant,
+ implicationStack);
+ }
+ return boogieExpr;
+ }
+
+ public static Expr AddConditionToCandidate(Expr boogieExpr, Expr condition, string candidateConstant)
+ {
+ return AddConditionToCandidateRec(boogieExpr, condition, candidateConstant, new List<Expr>());
+ }
+
+ public bool MatchCandidate(Expr boogieExpr, out Variable candidateConstant) {
+ candidateConstant = null;
+ string candidateString;
+ if(MatchCandidate(boogieExpr, houdiniConstants.Select(item => item.Name), out candidateString)) {
+ candidateConstant = houdiniConstants.Where(item => item.Name.Equals(candidateString)).ToList()[0];
+ return true;
+ }
+ return false;
+ }
+
+ public bool MatchCandidate(Expr boogieExpr, out string candidateConstant) {
+ return MatchCandidate(boogieExpr, houdiniConstants.Select(item => item.Name), out candidateConstant);
+ }
+
+ // For Explain houdini: it decorates the condition \phi as (vpos && (\phi || \not vneg))
+ // Precondition: MatchCandidate returns true
+ public Expr InsertCandidateControl(Expr boogieExpr, Variable vpos, Variable vneg)
+ {
+ Contract.Assert(CommandLineOptions.Clo.ExplainHoudini);
+
+ NAryExpr e = boogieExpr as NAryExpr;
+ if (e != null && e.Fun is BinaryOperator && ((BinaryOperator)e.Fun).Op == BinaryOperator.Opcode.Imp)
+ {
+ Expr antecedent = e.Args[0];
+ Expr consequent = e.Args[1];
+
+ IdentifierExpr id = antecedent as IdentifierExpr;
+ if (id != null && id.Decl is Constant && houdiniConstants.Contains((Constant)id.Decl))
+ {
+ return Expr.Imp(antecedent, Expr.And(Expr.Ident(vpos), Expr.Or(consequent, Expr.Not(Expr.Ident(vneg)))));
+ }
+
+ return Expr.Imp(antecedent, InsertCandidateControl(consequent, vpos, vneg));
+ }
+ Contract.Assert(false);
+ return null;
+ }
+
+ protected Dictionary<Variable, bool> BuildAssignment(HashSet<Variable> constants) {
+ Dictionary<Variable, bool> initial = new Dictionary<Variable, bool>();
+ foreach (var constant in constants)
+ initial.Add(constant, true);
+ return initial;
+ }
+
+ private bool IsOutcomeNotHoudini(ProverInterface.Outcome outcome, List<Counterexample> errors) {
+ switch (outcome) {
+ case ProverInterface.Outcome.Valid:
+ return false;
+ case ProverInterface.Outcome.Invalid:
+ Contract.Assume(errors != null);
+ foreach (Counterexample error in errors) {
+ if (ExtractRefutedAnnotation(error) == null)
+ return true;
+ }
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ // Record most current non-candidate errors found
+ // Return true if there was at least one non-candidate error
+ protected bool UpdateHoudiniOutcome(HoudiniOutcome houdiniOutcome,
+ Implementation implementation,
+ ProverInterface.Outcome outcome,
+ List<Counterexample> errors) {
+ string implName = implementation.Name;
+ houdiniOutcome.implementationOutcomes.Remove(implName);
+ List<Counterexample> nonCandidateErrors = new List<Counterexample>();
+
+ if (outcome == ProverInterface.Outcome.Invalid) {
+ foreach (Counterexample error in errors) {
+ if (ExtractRefutedAnnotation(error) == null)
+ nonCandidateErrors.Add(error);
+ }
+ }
+ houdiniOutcome.implementationOutcomes.Add(implName, new VCGenOutcome(outcome, nonCandidateErrors));
+ return nonCandidateErrors.Count > 0;
+ }
+
+ protected void FlushWorkList(int stage, IEnumerable<int> completedStages) {
+ this.NotifyFlushStart();
+ while (currentHoudiniState.WorkQueue.Count > 0) {
+ this.NotifyIteration();
+
+ currentHoudiniState.Implementation = currentHoudiniState.WorkQueue.Peek();
+ this.NotifyImplementation(currentHoudiniState.Implementation);
+
+ HoudiniSession session;
+ houdiniSessions.TryGetValue(currentHoudiniState.Implementation, out session);
+ List<Counterexample> errors;
+ ProverInterface.Outcome outcome = TryCatchVerify(session, stage, completedStages, out errors);
+ UpdateHoudiniOutcome(currentHoudiniState.Outcome, currentHoudiniState.Implementation, outcome, errors);
+ this.NotifyOutcome(outcome);
+
+ currentHoudiniState.WorkQueue.Dequeue();
+ this.NotifyDequeue();
+ }
+ this.NotifyFlushFinish();
+ }
+
+ protected void UpdateAssignment(RefutedAnnotation refAnnot) {
+ if (CommandLineOptions.Clo.Trace) {
+ Console.WriteLine("Removing " + refAnnot.Constant);
+ using (var cexWriter = new System.IO.StreamWriter(cexTraceFile, true))
+ cexWriter.WriteLine("Removing " + refAnnot.Constant);
+ }
+ currentHoudiniState.Assignment.Remove(refAnnot.Constant);
+ currentHoudiniState.Assignment.Add(refAnnot.Constant, false);
+ this.NotifyConstant(refAnnot.Constant.Name);
+ }
+
+ protected void AddRelatedToWorkList(RefutedAnnotation refutedAnnotation) {
+ Contract.Assume(currentHoudiniState.Implementation != null);
+ foreach (Implementation implementation in FindImplementationsToEnqueue(refutedAnnotation, refutedAnnotation.RefutationSite)) {
+ if (!currentHoudiniState.isBlackListed(implementation.Name)) {
+ currentHoudiniState.WorkQueue.Enqueue(implementation);
+ this.NotifyEnqueue(implementation);
+ }
+ }
+ }
+
+ // Updates the worklist and current assignment
+ // @return true if the current function is dequeued
+ protected bool UpdateAssignmentWorkList(ProverInterface.Outcome outcome,
+ List<Counterexample> errors) {
+ Contract.Assume(currentHoudiniState.Implementation != null);
+ bool dequeue = true;
+
+ switch (outcome) {
+ case ProverInterface.Outcome.Valid:
+ //yeah, dequeue
+ break;
+
+ case ProverInterface.Outcome.Invalid:
+ Contract.Assume(errors != null);
+
+ foreach (Counterexample error in errors) {
+ RefutedAnnotation refutedAnnotation = ExtractRefutedAnnotation(error);
+ if (refutedAnnotation != null) {
+ // some candidate annotation removed
+ ShareRefutedAnnotation(refutedAnnotation);
+ AddRelatedToWorkList(refutedAnnotation);
+ UpdateAssignment(refutedAnnotation);
+ dequeue = false;
+ #region Extra debugging output
+ if (CommandLineOptions.Clo.Trace) {
+ using (var cexWriter = new System.IO.StreamWriter(cexTraceFile, true)) {
+ cexWriter.WriteLine("Counter example for " + refutedAnnotation.Constant);
+ cexWriter.Write(error.ToString());
+ cexWriter.WriteLine();
+ using (var writer = new Microsoft.Boogie.TokenTextWriter(cexWriter, /*pretty=*/ false))
+ foreach (Microsoft.Boogie.Block blk in error.Trace)
+ blk.Emit(writer, 15);
+ //cexWriter.WriteLine();
+ }
+ }
+ #endregion
+ }
+ }
+
+ if (ExchangeRefutedAnnotations()) dequeue = false;
+
+ break;
+ default:
+ if (CommandLineOptions.Clo.Trace) {
+ Console.WriteLine("Timeout/Spaceout while verifying " + currentHoudiniState.Implementation.Name);
+ }
+ HoudiniSession houdiniSession;
+ houdiniSessions.TryGetValue(currentHoudiniState.Implementation, out houdiniSession);
+ foreach (Variable v in houdiniSession.houdiniAssertConstants) {
+ if (CommandLineOptions.Clo.Trace) {
+ Console.WriteLine("Removing " + v);
+ }
+ currentHoudiniState.Assignment.Remove(v);
+ currentHoudiniState.Assignment.Add(v, false);
+ this.NotifyConstant(v.Name);
+ }
+ currentHoudiniState.addToBlackList(currentHoudiniState.Implementation.Name);
+ break;
+ }
+
+ return dequeue;
+ }
+
+ // This method is a hook used by ConcurrentHoudini to
+ // exchange refuted annotations with other Houdini engines.
+ // If the method returns true, this indicates that at least
+ // one new refutation was received from some other engine.
+ // In the base class we thus return false.
+ protected virtual bool ExchangeRefutedAnnotations() {
+ return false;
+ }
+
+ // This method is a hook used by ConcurrentHoudini to
+ // apply a set of existing refuted annotations at the
+ // start of inference.
+ protected virtual void ApplyRefutedSharedAnnotations() {
+ // Empty in base class; can be overridden.
+ }
+
+ // This method is a hook used by ConcurrentHoudini to
+ // broadcast to other Houdini engines the fact that an
+ // annotation was refuted.
+ protected virtual void ShareRefutedAnnotation(RefutedAnnotation refutedAnnotation) {
+ // Empty in base class; can be overridden.
+ }
+
+ // Hook for ConcurrentHoudini, which requires a task id.
+ // Non-concurrent Houdini has -1 as a task id
+ protected virtual int GetTaskID() {
+ return -1;
+ }
+
+ public class WorkQueue {
+ private Queue<Implementation> queue;
+ private HashSet<Implementation> set;
+ public WorkQueue() {
+ queue = new Queue<Implementation>();
+ set = new HashSet<Implementation>();
+ }
+ public void Enqueue(Implementation impl) {
+ if (set.Contains(impl))
+ return;
+ queue.Enqueue(impl);
+ set.Add(impl);
+ }
+ public Implementation Dequeue() {
+ Implementation impl = queue.Dequeue();
+ set.Remove(impl);
+ return impl;
+ }
+ public Implementation Peek() {
+ return queue.Peek();
+ }
+ public int Count {
+ get { return queue.Count; }
+ }
+ public bool Contains(Implementation impl) {
+ return set.Contains(impl);
+ }
+ public WorkQueue Reverse()
+ {
+ var ret = new WorkQueue();
+ foreach (var impl in queue.Reverse())
+ ret.Enqueue(impl);
+ return ret;
+ }
+ }
+
+ public class HoudiniState {
+ public WorkQueue _workQueue;
+ public HashSet<string> blackList;
+ public Dictionary<Variable, bool> _assignment;
+ public Implementation _implementation;
+ public HoudiniOutcome _outcome;
+
+ public HoudiniState(WorkQueue workQueue, Dictionary<Variable, bool> currentAssignment) {
+ this._workQueue = workQueue;
+ this._assignment = currentAssignment;
+ this._implementation = null;
+ this._outcome = new HoudiniOutcome();
+ this.blackList = new HashSet<string>();
+ }
+
+ public WorkQueue WorkQueue {
+ get { return this._workQueue; }
+ }
+ public Dictionary<Variable, bool> Assignment {
+ get { return this._assignment; }
+ }
+ public Implementation Implementation {
+ get { return this._implementation; }
+ set { this._implementation = value; }
+ }
+ public HoudiniOutcome Outcome {
+ get { return this._outcome; }
+ }
+ public bool isBlackListed(string funcName) {
+ return blackList.Contains(funcName);
+ }
+ public void addToBlackList(string funcName) {
+ blackList.Add(funcName);
+ }
+ }
+
+ public HoudiniOutcome PerformHoudiniInference(int stage = 0,
+ IEnumerable<int> completedStages = null,
+ Dictionary<string, bool> initialAssignment = null) {
+ this.NotifyStart(program, houdiniConstants.Count);
+
+ currentHoudiniState = new HoudiniState(BuildWorkList(program), BuildAssignment(houdiniConstants));
+
+ if(initialAssignment != null) {
+ foreach(var v in CurrentHoudiniState.Assignment.Keys.ToList()) {
+ CurrentHoudiniState.Assignment[v] = initialAssignment[v.Name];
+ }
+ }
+
+ ApplyRefutedSharedAnnotations();
+
+ foreach (Implementation impl in vcgenFailures) {
+ currentHoudiniState.addToBlackList(impl.Name);
+ }
+
+ while (currentHoudiniState.WorkQueue.Count > 0) {
+ this.NotifyIteration();
+
+ currentHoudiniState.Implementation = currentHoudiniState.WorkQueue.Peek();
+ this.NotifyImplementation(currentHoudiniState.Implementation);
+
+ HoudiniSession session;
+ this.houdiniSessions.TryGetValue(currentHoudiniState.Implementation, out session);
+ HoudiniVerifyCurrent(session, stage, completedStages);
+ }
+
+ this.NotifyEnd(true);
+ Dictionary<string, bool> assignment = new Dictionary<string, bool>();
+ foreach (var x in currentHoudiniState.Assignment)
+ assignment[x.Key.Name] = x.Value;
+ currentHoudiniState.Outcome.assignment = assignment;
+ return currentHoudiniState.Outcome;
+ }
+
+ public void Close() {
+ vcgen.Close();
+ proverInterface.Close();
+ if (CommandLineOptions.Clo.ExplainHoudini)
+ {
+ explainHoudiniDottyFile.WriteLine("};");
+ explainHoudiniDottyFile.Close();
+ }
+ }
+
+ private int NumberOfStages()
+ {
+ int result = 1;
+ foreach(var c in program.Constants) {
+ result = Math.Max(result, 1 + QKeyValue.FindIntAttribute(c.Attributes, "stage_active", -1));
+ }
+ return result;
+ }
+
+ private List<Implementation> FindImplementationsToEnqueue(RefutedAnnotation refutedAnnotation, Implementation currentImplementation) {
+ HoudiniSession session;
+ List<Implementation> implementations = new List<Implementation>();
+ switch (refutedAnnotation.Kind) {
+ case RefutedAnnotationKind.REQUIRES:
+ foreach (Implementation callee in callGraph.Successors(currentImplementation))
+ {
+ if (vcgenFailures.Contains(callee)) continue;
+ houdiniSessions.TryGetValue(callee, out session);
+ Contract.Assume(callee.Proc != null);
+ if (callee.Proc.Equals(refutedAnnotation.CalleeProc) && session.InUnsatCore(refutedAnnotation.Constant))
+ implementations.Add(callee);
+ }
+ break;
+ case RefutedAnnotationKind.ENSURES:
+ foreach (Implementation caller in callGraph.Predecessors(currentImplementation))
+ {
+ if (vcgenFailures.Contains(caller)) continue;
+ houdiniSessions.TryGetValue(caller, out session);
+ if (session.InUnsatCore(refutedAnnotation.Constant))
+ implementations.Add(caller);
+ }
+ break;
+ case RefutedAnnotationKind.ASSERT: //the implementation is already in queue
+ if (CommandLineOptions.Clo.HoudiniUseCrossDependencies && crossDependencies.assumedInImpl.ContainsKey(refutedAnnotation.Constant.Name))
+ {
+ foreach (var impl in crossDependencies.assumedInImpl[refutedAnnotation.Constant.Name])
+ {
+ if (vcgenFailures.Contains(impl)) continue;
+ houdiniSessions.TryGetValue(impl, out session);
+ if (session.InUnsatCore(refutedAnnotation.Constant))
+ implementations.Add(impl);
+ }
+ }
+ break;
+ default:
+ throw new Exception("Unknown Refuted annotation kind:" + refutedAnnotation.Kind);
+ }
+ return implementations;
+ }
+
+ public enum RefutedAnnotationKind { REQUIRES, ENSURES, ASSERT };
+
+ public class RefutedAnnotation {
+ private Variable _constant;
+ private RefutedAnnotationKind _kind;
+ private Procedure _callee;
+ private Implementation _refutationSite;
+
+ private RefutedAnnotation(Variable constant, RefutedAnnotationKind kind, Procedure callee, Implementation refutationSite) {
+ this._constant = constant;
+ this._kind = kind;
+ this._callee = callee;
+ this._refutationSite = refutationSite;
+ }
+ public RefutedAnnotationKind Kind {
+ get { return this._kind; }
+ }
+ public Variable Constant {
+ get { return this._constant; }
+ }
+ public Procedure CalleeProc {
+ get { return this._callee; }
+ }
+ public Implementation RefutationSite {
+ get { return this._refutationSite; }
+ }
+ public static RefutedAnnotation BuildRefutedRequires(Variable constant, Procedure callee, Implementation refutationSite) {
+ return new RefutedAnnotation(constant, RefutedAnnotationKind.REQUIRES, callee, refutationSite);
+ }
+ public static RefutedAnnotation BuildRefutedEnsures(Variable constant, Implementation refutationSite) {
+ return new RefutedAnnotation(constant, RefutedAnnotationKind.ENSURES, null, refutationSite);
+ }
+ public static RefutedAnnotation BuildRefutedAssert(Variable constant, Implementation refutationSite) {
+ return new RefutedAnnotation(constant, RefutedAnnotationKind.ASSERT, null, refutationSite);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked {
+ int hash = 17;
+ hash = hash * 23 + this.Constant.GetHashCode();
+ hash = hash * 23 + this.Kind.GetHashCode();
+ if (this.CalleeProc != null)
+ hash = hash * 23 + this.CalleeProc.GetHashCode();
+ hash = hash * 23 + this.RefutationSite.GetHashCode();
+ return hash;
+ }
+ }
+
+ public override bool Equals(object obj) {
+ bool result = true;
+ var other = obj as RefutedAnnotation;
+
+ if (other == null) {
+ result = false;
+ } else {
+ result = result && String.Equals(other.Constant, this.Constant);
+ result = result && String.Equals(other.Kind, this.Kind);
+ if (other.CalleeProc != null && this.CalleeProc != null)
+ result = result && String.Equals(other.CalleeProc, this.CalleeProc);
+ result = result && String.Equals(other.RefutationSite, this.RefutationSite);
+ }
+
+ return result;
+ }
+ }
+
+ private void PrintRefutedCall(CallCounterexample err, XmlSink xmlOut) {
+ Expr cond = err.FailingRequires.Condition;
+ Variable houdiniConst;
+ if (MatchCandidate(cond, out houdiniConst)) {
+ xmlOut.WriteError("precondition violation", err.FailingCall.tok, err.FailingRequires.tok, err.Trace);
+ }
+ }
+
+ private void PrintRefutedReturn(ReturnCounterexample err, XmlSink xmlOut) {
+ Expr cond = err.FailingEnsures.Condition;
+ Variable houdiniConst;
+ if (MatchCandidate(cond, out houdiniConst)) {
+ xmlOut.WriteError("postcondition violation", err.FailingReturn.tok, err.FailingEnsures.tok, err.Trace);
+ }
+ }
+
+ private void PrintRefutedAssert(AssertCounterexample err, XmlSink xmlOut) {
+ Expr cond = err.FailingAssert.OrigExpr;
+ Variable houdiniConst;
+ if (MatchCandidate(cond, out houdiniConst)) {
+ xmlOut.WriteError("postcondition violation", err.FailingAssert.tok, err.FailingAssert.tok, err.Trace);
+ }
+ }
+
+ protected void DebugRefutedCandidates(Implementation curFunc, List<Counterexample> errors) {
+ XmlSink xmlRefuted = CommandLineOptions.Clo.XmlRefuted;
+ if (xmlRefuted != null && errors != null) {
+ DateTime start = DateTime.UtcNow;
+ xmlRefuted.WriteStartMethod(curFunc.ToString(), start);
+
+ foreach (Counterexample error in errors) {
+ CallCounterexample ce = error as CallCounterexample;
+ if (ce != null) PrintRefutedCall(ce, xmlRefuted);
+ ReturnCounterexample re = error as ReturnCounterexample;
+ if (re != null) PrintRefutedReturn(re, xmlRefuted);
+ AssertCounterexample ae = error as AssertCounterexample;
+ if (ae != null) PrintRefutedAssert(ae, xmlRefuted);
+ }
+
+ DateTime end = DateTime.UtcNow;
+ xmlRefuted.WriteEndMethod("errors", end, end.Subtract(start));
+ }
+ }
+
+ private RefutedAnnotation ExtractRefutedAnnotation(Counterexample error) {
+ Variable houdiniConstant;
+ CallCounterexample callCounterexample = error as CallCounterexample;
+ if (callCounterexample != null) {
+ Procedure failingProcedure = callCounterexample.FailingCall.Proc;
+ Requires failingRequires = callCounterexample.FailingRequires;
+ if (MatchCandidate(failingRequires.Condition, out houdiniConstant)) {
+ Contract.Assert(houdiniConstant != null);
+ return RefutedAnnotation.BuildRefutedRequires(houdiniConstant, failingProcedure, currentHoudiniState.Implementation);
+ }
+ }
+ ReturnCounterexample returnCounterexample = error as ReturnCounterexample;
+ if (returnCounterexample != null) {
+ Ensures failingEnsures = returnCounterexample.FailingEnsures;
+ if (MatchCandidate(failingEnsures.Condition, out houdiniConstant)) {
+ Contract.Assert(houdiniConstant != null);
+ return RefutedAnnotation.BuildRefutedEnsures(houdiniConstant, currentHoudiniState.Implementation);
+ }
+ }
+ AssertCounterexample assertCounterexample = error as AssertCounterexample;
+ if (assertCounterexample != null) {
+ AssertCmd failingAssert = assertCounterexample.FailingAssert;
+ if (MatchCandidate(failingAssert.OrigExpr, out houdiniConstant)) {
+ Contract.Assert(houdiniConstant != null);
+ return RefutedAnnotation.BuildRefutedAssert(houdiniConstant, currentHoudiniState.Implementation);
+ }
+ }
+
+ return null;
+ }
+
+ private ProverInterface.Outcome TryCatchVerify(HoudiniSession session, int stage, IEnumerable<int> completedStages, out List<Counterexample> errors) {
+ ProverInterface.Outcome outcome;
+ try {
+ outcome = session.Verify(proverInterface, GetAssignmentWithStages(stage, completedStages), out errors, taskID: GetTaskID());
+ }
+ catch (UnexpectedProverOutputException upo) {
+ Contract.Assume(upo != null);
+ errors = null;
+ outcome = ProverInterface.Outcome.Undetermined;
+ }
+ return outcome;
+ }
+
+ protected Dictionary<Variable, bool> GetAssignmentWithStages(int currentStage, IEnumerable<int> completedStages)
+ {
+ Dictionary<Variable, bool> result = new Dictionary<Variable, bool>(currentHoudiniState.Assignment);
+ foreach (var c in program.Constants)
+ {
+ int stageActive = QKeyValue.FindIntAttribute(c.Attributes, "stage_active", -1);
+ if (stageActive != -1)
+ {
+ result[c] = (stageActive == currentStage);
+ }
+ int stageComplete = QKeyValue.FindIntAttribute(c.Attributes, "stage_complete", -1);
+ if (stageComplete != -1)
+ {
+ result[c] = (completedStages.Contains(stageComplete));
+ }
+ }
+ return result;
+ }
+
+ private void HoudiniVerifyCurrent(HoudiniSession session, int stage, IEnumerable<int> completedStages) {
+ while (true) {
+ this.NotifyAssignment(currentHoudiniState.Assignment);
+
+ //check the VC with the current assignment
+ List<Counterexample> errors;
+ ProverInterface.Outcome outcome = TryCatchVerify(session, stage, completedStages, out errors);
+ this.NotifyOutcome(outcome);
+
+ DebugRefutedCandidates(currentHoudiniState.Implementation, errors);
+
+ #region Explain Houdini
+ if (CommandLineOptions.Clo.ExplainHoudini && outcome == ProverInterface.Outcome.Invalid)
+ {
+ Contract.Assume(errors != null);
+ // make a copy of this variable
+ errors = new List<Counterexample>(errors);
+ var refutedAnnotations = new List<RefutedAnnotation>();
+ foreach (Counterexample error in errors)
+ {
+ RefutedAnnotation refutedAnnotation = ExtractRefutedAnnotation(error);
+ if (refutedAnnotation == null || refutedAnnotation.Kind == RefutedAnnotationKind.ASSERT) continue;
+ refutedAnnotations.Add(refutedAnnotation);
+ }
+ foreach (var refutedAnnotation in refutedAnnotations)
+ {
+ session.Explain(proverInterface, currentHoudiniState.Assignment, refutedAnnotation.Constant);
+ }
+ }
+ #endregion
+
+ if (UpdateHoudiniOutcome(currentHoudiniState.Outcome, currentHoudiniState.Implementation, outcome, errors)) { // abort
+ currentHoudiniState.WorkQueue.Dequeue();
+ this.NotifyDequeue();
+ FlushWorkList(stage, completedStages);
+ return;
+ }
+ else if (UpdateAssignmentWorkList(outcome, errors)) {
+ if (CommandLineOptions.Clo.UseUnsatCoreForContractInfer && outcome == ProverInterface.Outcome.Valid)
+ session.UpdateUnsatCore(proverInterface, currentHoudiniState.Assignment);
+ currentHoudiniState.WorkQueue.Dequeue();
+ this.NotifyDequeue();
+ return;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Transforms given program based on Houdini outcome. If a constant is assigned "true",
+ /// any preconditions or postconditions guarded by the constant are made free, and any assertions
+ /// guarded by the constant are replaced with assumptions.
+ ///
+ /// If a constant is assigned "false", any preconditions or postconditions
+ /// guarded by the constant are replaced with "true", and assertions guarded by the constant
+ /// are removed.
+ ///
+ /// In addition, all Houdini constants are removed from the program.
+ /// </summary>
+ public static void ApplyAssignment(Program prog, HoudiniOutcome outcome) {
+
+ var Candidates = prog.TopLevelDeclarations.OfType<Constant>().Where
+ (Item => QKeyValue.FindBoolAttribute(Item.Attributes, "existential")).Select(Item => Item.Name);
+
+ // Treat all assertions
+ // TODO: do we need to also consider assumptions?
+ foreach (Block block in prog.Implementations.Select(item => item.Blocks).SelectMany(item => item)) {
+ List<Cmd> newCmds = new List<Cmd>();
+ foreach (Cmd cmd in block.Cmds) {
+ string c;
+ AssertCmd assertCmd = cmd as AssertCmd;
+ if (assertCmd != null && MatchCandidate(assertCmd.Expr, Candidates, out c)) {
+ var cVar = outcome.assignment.Keys.Where(item => item.Equals(c)).ToList()[0];
+ if (outcome.assignment[cVar]) {
+ Dictionary<Variable, Expr> cToTrue = new Dictionary<Variable, Expr>();
+ Variable cVarProg = prog.Variables.Where(item => item.Name.Equals(c)).ToList()[0];
+ cToTrue[cVarProg] = Expr.True;
+ newCmds.Add(new AssumeCmd(assertCmd.tok,
+ Substituter.Apply(Substituter.SubstitutionFromHashtable(cToTrue), assertCmd.Expr),
+ assertCmd.Attributes));
+ }
+ }
+ else {
+ newCmds.Add(cmd);
+ }
+ }
+ block.Cmds = newCmds;
+ }
+
+ foreach (var proc in prog.Procedures) {
+ List<Requires> newRequires = new List<Requires>();
+ foreach (Requires r in proc.Requires) {
+ string c;
+ if (MatchCandidate(r.Condition, Candidates, out c)) {
+ var cVar = outcome.assignment.Keys.Where(item => item.Equals(c)).ToList()[0];
+ if (outcome.assignment[cVar]) {
+ Variable cVarProg = prog.Variables.Where(item => item.Name.Equals(c)).ToList()[0];
+ Dictionary<Variable, Expr> subst = new Dictionary<Variable, Expr>();
+ subst[cVarProg] = Expr.True;
+ newRequires.Add(new Requires(Token.NoToken, true,
+ Substituter.Apply(Substituter.SubstitutionFromHashtable(subst), r.Condition),
+ r.Comment, r.Attributes));
+ }
+ }
+ else {
+ newRequires.Add(r);
+ }
+ }
+ proc.Requires = newRequires;
+
+ List<Ensures> newEnsures = new List<Ensures>();
+ foreach (Ensures e in proc.Ensures) {
+ string c;
+ if (MatchCandidate(e.Condition, Candidates, out c)) {
+ var cVar = outcome.assignment.Keys.Where(item => item.Equals(c)).ToList()[0];
+ if (outcome.assignment[cVar]) {
+ Variable cVarProg = prog.Variables.Where(item => item.Name.Equals(c)).ToList()[0];
+ Dictionary<Variable, Expr> subst = new Dictionary<Variable, Expr>();
+ subst[cVarProg] = Expr.True;
+ newEnsures.Add(new Ensures(Token.NoToken, true,
+ Substituter.Apply(Substituter.SubstitutionFromHashtable(subst), e.Condition),
+ e.Comment, e.Attributes));
+ }
+ }
+ else {
+ newEnsures.Add(e);
+ }
+ }
+ proc.Ensures = newEnsures;
+ }
+
+ // Remove the existential constants
+ prog.RemoveTopLevelDeclarations(item => (item is Constant) &&
+ (Candidates.Any(item2 => item2.Equals((item as Constant).Name))));
+ }
+
+ }
+
+ public class VCGenOutcome {
+ public VCGen.Outcome outcome;
+ public List<Counterexample> errors;
+ public VCGenOutcome(ProverInterface.Outcome outcome, List<Counterexample> errors) {
+ this.outcome = ConditionGeneration.ProverInterfaceOutcomeToConditionGenerationOutcome(outcome);
+ this.errors = errors;
+ }
+ }
+
+ public class HoudiniOutcome {
+ // final assignment
+ public Dictionary<string, bool> assignment = new Dictionary<string, bool>();
+ // boogie errors
+ public Dictionary<string, VCGenOutcome> implementationOutcomes = new Dictionary<string, VCGenOutcome>();
+
+ // statistics
+
+ private int CountResults(VCGen.Outcome outcome) {
+ int outcomeCount = 0;
+ foreach (VCGenOutcome verifyOutcome in implementationOutcomes.Values) {
+ if (verifyOutcome.outcome == outcome)
+ outcomeCount++;
+ }
+ return outcomeCount;
+ }
+
+ private List<string> ListOutcomeMatches(VCGen.Outcome outcome) {
+ List<string> result = new List<string>();
+ foreach (KeyValuePair<string, VCGenOutcome> kvpair in implementationOutcomes) {
+ if (kvpair.Value.outcome == outcome)
+ result.Add(kvpair.Key);
+ }
+ return result;
+ }
+
+ public int ErrorCount {
+ get {
+ return CountResults(VCGen.Outcome.Errors);
+ }
+ }
+ public int Verified {
+ get {
+ return CountResults(VCGen.Outcome.Correct);
+ }
+ }
+ public int Inconclusives {
+ get {
+ return CountResults(VCGen.Outcome.Inconclusive);
+ }
+ }
+ public int TimeOuts {
+ get {
+ return CountResults(VCGen.Outcome.TimedOut);
+ }
+ }
+ public List<string> ListOfTimeouts {
+ get {
+ return ListOutcomeMatches(VCGen.Outcome.TimedOut);
+ }
+ }
+ public List<string> ListOfInconclusives {
+ get {
+ return ListOutcomeMatches(VCGen.Outcome.Inconclusive);
+ }
+ }
+ public List<string> ListOfErrors {
+ get {
+ return ListOutcomeMatches(VCGen.Outcome.Errors);
+ }
+ }
+ }
+
+}
diff --git a/Source/Houdini/Houdini.csproj b/Source/Houdini/Houdini.csproj
index dcb42ef7..68ea62e3 100644
--- a/Source/Houdini/Houdini.csproj
+++ b/Source/Houdini/Houdini.csproj
@@ -1,154 +1,154 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <PropertyGroup>
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
- <ProductVersion>8.0.30703</ProductVersion>
- <SchemaVersion>2.0</SchemaVersion>
- <ProjectGuid>{CF41E903-78EB-43BA-A355-E5FEB5ECECD4}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>Microsoft.Boogie.Houdini</RootNamespace>
- <AssemblyName>Houdini</AssemblyName>
- <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
- <FileAlignment>512</FileAlignment>
- <CodeContractsAssemblyMode>0</CodeContractsAssemblyMode>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
- <DebugSymbols>true</DebugSymbols>
- <DebugType>full</DebugType>
- <Optimize>false</Optimize>
- <OutputPath>bin\Debug\</OutputPath>
- <DefineConstants>DEBUG;TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
- <DebugType>pdbonly</DebugType>
- <Optimize>true</Optimize>
- <OutputPath>bin\Release\</OutputPath>
- <DefineConstants>TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- </PropertyGroup>
- <PropertyGroup>
- <SignAssembly>true</SignAssembly>
- </PropertyGroup>
- <PropertyGroup>
- <AssemblyOriginatorKeyFile>..\InterimKey.snk</AssemblyOriginatorKeyFile>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Checked|AnyCPU'">
- <OutputPath>bin\Checked\</OutputPath>
- <CodeContractsEnableRuntimeChecking>True</CodeContractsEnableRuntimeChecking>
- <CodeContractsRuntimeOnlyPublicSurface>False</CodeContractsRuntimeOnlyPublicSurface>
- <CodeContractsRuntimeThrowOnFailure>True</CodeContractsRuntimeThrowOnFailure>
- <CodeContractsRuntimeCallSiteRequires>False</CodeContractsRuntimeCallSiteRequires>
- <CodeContractsRuntimeSkipQuantifiers>False</CodeContractsRuntimeSkipQuantifiers>
- <CodeContractsRunCodeAnalysis>False</CodeContractsRunCodeAnalysis>
- <CodeContractsNonNullObligations>False</CodeContractsNonNullObligations>
- <CodeContractsBoundsObligations>False</CodeContractsBoundsObligations>
- <CodeContractsArithmeticObligations>False</CodeContractsArithmeticObligations>
- <CodeContractsEnumObligations>False</CodeContractsEnumObligations>
- <CodeContractsRedundantAssumptions>False</CodeContractsRedundantAssumptions>
- <CodeContractsRunInBackground>True</CodeContractsRunInBackground>
- <CodeContractsShowSquigglies>False</CodeContractsShowSquigglies>
- <CodeContractsUseBaseLine>False</CodeContractsUseBaseLine>
- <CodeContractsEmitXMLDocs>False</CodeContractsEmitXMLDocs>
- <CodeContractsCustomRewriterAssembly>
- </CodeContractsCustomRewriterAssembly>
- <CodeContractsCustomRewriterClass>
- </CodeContractsCustomRewriterClass>
- <CodeContractsLibPaths>
- </CodeContractsLibPaths>
- <CodeContractsExtraRewriteOptions>
- </CodeContractsExtraRewriteOptions>
- <CodeContractsExtraAnalysisOptions>
- </CodeContractsExtraAnalysisOptions>
- <CodeContractsBaseLineFile>
- </CodeContractsBaseLineFile>
- <CodeContractsCacheAnalysisResults>False</CodeContractsCacheAnalysisResults>
- <CodeContractsRuntimeCheckingLevel>Full</CodeContractsRuntimeCheckingLevel>
- <CodeContractsReferenceAssembly>Build</CodeContractsReferenceAssembly>
- <CodeContractsAnalysisWarningLevel>0</CodeContractsAnalysisWarningLevel>
- <WarningLevel>4</WarningLevel>
- <Optimize>false</Optimize>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'QED|AnyCPU'">
- <DebugSymbols>true</DebugSymbols>
- <OutputPath>bin\QED\</OutputPath>
- <DefineConstants>DEBUG;TRACE</DefineConstants>
- <DebugType>full</DebugType>
- <PlatformTarget>AnyCPU</PlatformTarget>
- <ErrorReport>prompt</ErrorReport>
- <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
- </PropertyGroup>
- <ItemGroup>
- <Reference Include="System" />
- <Reference Include="System.Core" />
- <Reference Include="System.Xml.Linq" />
- <Reference Include="System.Data.DataSetExtensions" />
- <Reference Include="Microsoft.CSharp" />
- <Reference Include="System.Data" />
- <Reference Include="System.Xml" />
- </ItemGroup>
- <ItemGroup>
- <Compile Include="..\version.cs">
- <Link>version.cs</Link>
- </Compile>
- <Compile Include="AbstractHoudini.cs" />
- <Compile Include="Checker.cs" />
- <Compile Include="AnnotationDependenceAnalyser.cs" />
- <Compile Include="Houdini.cs" />
- <Compile Include="StagedHoudini.cs" />
- <Compile Include="ConcurrentHoudini.cs" />
- </ItemGroup>
- <ItemGroup>
- <ProjectReference Include="..\Basetypes\Basetypes.csproj">
- <Project>{43DFAD18-3E35-4558-9BE2-CAFF6B5BA8A0}</Project>
- <Name>Basetypes</Name>
- </ProjectReference>
- <ProjectReference Include="..\CodeContractsExtender\CodeContractsExtender.csproj">
- <Project>{ACCC0156-0921-43ED-8F67-AD8BDC8CDE31}</Project>
- <Name>CodeContractsExtender</Name>
- </ProjectReference>
- <ProjectReference Include="..\Core\Core.csproj">
- <Project>{B230A69C-C466-4065-B9C1-84D80E76D802}</Project>
- <Name>Core</Name>
- </ProjectReference>
- <ProjectReference Include="..\Graph\Graph.csproj">
- <Project>{69A2B0B8-BCAC-4101-AE7A-556FCC58C06E}</Project>
- <Name>Graph</Name>
- </ProjectReference>
- <ProjectReference Include="..\Model\Model.csproj">
- <Project>{ACEF88D5-DADD-46DA-BAE1-2144D63F4C83}</Project>
- <Name>Model</Name>
- </ProjectReference>
- <ProjectReference Include="..\ParserHelper\ParserHelper.csproj">
- <Project>{FCD3AC7F-9DFD-46C8-AB1E-09F0B0F16DC5}</Project>
- <Name>ParserHelper</Name>
- </ProjectReference>
- <ProjectReference Include="..\Provers\SMTLib\SMTLib.csproj">
- <Project>{9B163AA3-36BC-4AFB-88AB-79BC9E97E401}</Project>
- <Name>SMTLib</Name>
- </ProjectReference>
- <ProjectReference Include="..\VCExpr\VCExpr.csproj">
- <Project>{56FFDBCA-7D14-43B8-A6CA-22A20E417EE1}</Project>
- <Name>VCExpr</Name>
- </ProjectReference>
- <ProjectReference Include="..\VCGeneration\VCGeneration.csproj">
- <Project>{E1F10180-C7B9-4147-B51F-FA1B701966DC}</Project>
- <Name>VCGeneration</Name>
- </ProjectReference>
- </ItemGroup>
- <ItemGroup>
- <Folder Include="Properties\" />
- </ItemGroup>
- <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
- <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
- Other similar extension points exist, see Microsoft.Common.targets.
- <Target Name="BeforeBuild">
- </Target>
- <Target Name="AfterBuild">
- </Target>
- -->
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>8.0.30703</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{CF41E903-78EB-43BA-A355-E5FEB5ECECD4}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Microsoft.Boogie.Houdini</RootNamespace>
+ <AssemblyName>Houdini</AssemblyName>
+ <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <CodeContractsAssemblyMode>0</CodeContractsAssemblyMode>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup>
+ <SignAssembly>true</SignAssembly>
+ </PropertyGroup>
+ <PropertyGroup>
+ <AssemblyOriginatorKeyFile>..\InterimKey.snk</AssemblyOriginatorKeyFile>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Checked|AnyCPU'">
+ <OutputPath>bin\Checked\</OutputPath>
+ <CodeContractsEnableRuntimeChecking>True</CodeContractsEnableRuntimeChecking>
+ <CodeContractsRuntimeOnlyPublicSurface>False</CodeContractsRuntimeOnlyPublicSurface>
+ <CodeContractsRuntimeThrowOnFailure>True</CodeContractsRuntimeThrowOnFailure>
+ <CodeContractsRuntimeCallSiteRequires>False</CodeContractsRuntimeCallSiteRequires>
+ <CodeContractsRuntimeSkipQuantifiers>False</CodeContractsRuntimeSkipQuantifiers>
+ <CodeContractsRunCodeAnalysis>False</CodeContractsRunCodeAnalysis>
+ <CodeContractsNonNullObligations>False</CodeContractsNonNullObligations>
+ <CodeContractsBoundsObligations>False</CodeContractsBoundsObligations>
+ <CodeContractsArithmeticObligations>False</CodeContractsArithmeticObligations>
+ <CodeContractsEnumObligations>False</CodeContractsEnumObligations>
+ <CodeContractsRedundantAssumptions>False</CodeContractsRedundantAssumptions>
+ <CodeContractsRunInBackground>True</CodeContractsRunInBackground>
+ <CodeContractsShowSquigglies>False</CodeContractsShowSquigglies>
+ <CodeContractsUseBaseLine>False</CodeContractsUseBaseLine>
+ <CodeContractsEmitXMLDocs>False</CodeContractsEmitXMLDocs>
+ <CodeContractsCustomRewriterAssembly>
+ </CodeContractsCustomRewriterAssembly>
+ <CodeContractsCustomRewriterClass>
+ </CodeContractsCustomRewriterClass>
+ <CodeContractsLibPaths>
+ </CodeContractsLibPaths>
+ <CodeContractsExtraRewriteOptions>
+ </CodeContractsExtraRewriteOptions>
+ <CodeContractsExtraAnalysisOptions>
+ </CodeContractsExtraAnalysisOptions>
+ <CodeContractsBaseLineFile>
+ </CodeContractsBaseLineFile>
+ <CodeContractsCacheAnalysisResults>False</CodeContractsCacheAnalysisResults>
+ <CodeContractsRuntimeCheckingLevel>Full</CodeContractsRuntimeCheckingLevel>
+ <CodeContractsReferenceAssembly>Build</CodeContractsReferenceAssembly>
+ <CodeContractsAnalysisWarningLevel>0</CodeContractsAnalysisWarningLevel>
+ <WarningLevel>4</WarningLevel>
+ <Optimize>false</Optimize>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'QED|AnyCPU'">
+ <DebugSymbols>true</DebugSymbols>
+ <OutputPath>bin\QED\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <DebugType>full</DebugType>
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <ErrorReport>prompt</ErrorReport>
+ <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="..\version.cs">
+ <Link>version.cs</Link>
+ </Compile>
+ <Compile Include="AbstractHoudini.cs" />
+ <Compile Include="Checker.cs" />
+ <Compile Include="AnnotationDependenceAnalyser.cs" />
+ <Compile Include="Houdini.cs" />
+ <Compile Include="StagedHoudini.cs" />
+ <Compile Include="ConcurrentHoudini.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Basetypes\Basetypes.csproj">
+ <Project>{43DFAD18-3E35-4558-9BE2-CAFF6B5BA8A0}</Project>
+ <Name>Basetypes</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\CodeContractsExtender\CodeContractsExtender.csproj">
+ <Project>{ACCC0156-0921-43ED-8F67-AD8BDC8CDE31}</Project>
+ <Name>CodeContractsExtender</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\Core\Core.csproj">
+ <Project>{B230A69C-C466-4065-B9C1-84D80E76D802}</Project>
+ <Name>Core</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\Graph\Graph.csproj">
+ <Project>{69A2B0B8-BCAC-4101-AE7A-556FCC58C06E}</Project>
+ <Name>Graph</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\Model\Model.csproj">
+ <Project>{ACEF88D5-DADD-46DA-BAE1-2144D63F4C83}</Project>
+ <Name>Model</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\ParserHelper\ParserHelper.csproj">
+ <Project>{FCD3AC7F-9DFD-46C8-AB1E-09F0B0F16DC5}</Project>
+ <Name>ParserHelper</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\Provers\SMTLib\SMTLib.csproj">
+ <Project>{9B163AA3-36BC-4AFB-88AB-79BC9E97E401}</Project>
+ <Name>SMTLib</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\VCExpr\VCExpr.csproj">
+ <Project>{56FFDBCA-7D14-43B8-A6CA-22A20E417EE1}</Project>
+ <Name>VCExpr</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\VCGeneration\VCGeneration.csproj">
+ <Project>{E1F10180-C7B9-4147-B51F-FA1B701966DC}</Project>
+ <Name>VCGeneration</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <Folder Include="Properties\" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
</Project> \ No newline at end of file
diff --git a/Source/Houdini/StagedHoudini.cs b/Source/Houdini/StagedHoudini.cs
index 27481e4f..964321c7 100644
--- a/Source/Houdini/StagedHoudini.cs
+++ b/Source/Houdini/StagedHoudini.cs
@@ -1,427 +1,427 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Diagnostics.Contracts;
-using System.Diagnostics;
-using System.Threading.Tasks;
-using System.Threading;
-using Microsoft.Boogie;
-using Microsoft.Boogie.GraphUtil;
-
-namespace Microsoft.Boogie.Houdini
-{
- public class StagedHoudini
- {
-
- private Program program;
- private HoudiniSession.HoudiniStatistics houdiniStats;
- private Func<string, Program> ProgramFromFile;
- private StagedHoudiniPlan plan;
- private List<Houdini>[] houdiniInstances;
- private List<StagedHoudiniTask> tasks = new List<StagedHoudiniTask>();
- private Dictionary<ScheduledStage, HoudiniOutcome> outcomes = new Dictionary<ScheduledStage,HoudiniOutcome>();
-
- private const string tempFilename = "__stagedHoudiniTemp.bpl";
-
- public StagedHoudini(Program program, HoudiniSession.HoudiniStatistics houdiniStats, Func<string, Program> ProgramFromFile) {
- this.program = program;
- this.houdiniStats = houdiniStats;
- this.ProgramFromFile = ProgramFromFile;
- this.houdiniInstances = new List<Houdini>[CommandLineOptions.Clo.StagedHoudiniThreads];
- for (int i = 0; i < CommandLineOptions.Clo.StagedHoudiniThreads; i++) {
- houdiniInstances[i] = new List<Houdini>();
- }
-
- BreakApartConjunctionsInAnnotations();
-
- var annotationDependenceAnalyser = new AnnotationDependenceAnalyser(program);
- annotationDependenceAnalyser.Analyse();
- this.plan = annotationDependenceAnalyser.ApplyStages();
- if (CommandLineOptions.Clo.Trace)
- {
- annotationDependenceAnalyser.dump();
-
- if(CommandLineOptions.Clo.DebugStagedHoudini) {
- Console.WriteLine("Plan\n====\n");
- if(plan == null) {
- Console.WriteLine("No plan, as there were no stages");
- } else {
- Console.WriteLine(this.plan);
- }
- }
-
- EmitProgram("staged.bpl");
- }
- }
-
- private void BreakApartConjunctionsInAnnotations()
- {
- // StagedHoudini works on a syntactic basis, so that
- // if x and y occur in the same annotation, any annotation
- // referring to either x or y will be in the same stage
- // as this annotation. It is thus desirable to separate
- // conjunctive annotations into multiple annotations,
- // to reduce these syntactic dependencies.
-
- foreach(var b in program.Blocks()) {
- List<Cmd> newCmds = new List<Cmd>();
- foreach(var c in b.Cmds) {
- var assertion = c as AssertCmd;
- if (assertion != null) {
- foreach(var e in BreakIntoConjuncts(assertion.Expr)) {
- newCmds.Add(new AssertCmd(assertion.tok, e, assertion.Attributes));
- }
- } else {
- newCmds.Add(c);
- }
- }
- b.Cmds = newCmds;
- }
-
- foreach(var proc in program.Procedures) {
- {
- var newRequires = new List<Requires>();
- foreach(var r in proc.Requires) {
- foreach(var c in BreakIntoConjuncts(r.Condition)) {
- newRequires.Add(new Requires(r.tok, r.Free, c, r.Comment, r.Attributes));
- }
- }
- proc.Requires = newRequires;
- }
- {
- var newEnsures = new List<Ensures>();
- foreach(var e in proc.Ensures) {
- foreach(var c in BreakIntoConjuncts(e.Condition)) {
- newEnsures.Add(new Ensures(e.tok, e.Free, c, e.Comment, e.Attributes));
- }
- }
- proc.Ensures = newEnsures;
- }
- }
- }
-
- private List<Expr> BreakIntoConjuncts(Expr expr)
- {
- var nary = expr as NAryExpr;
- if(nary == null) {
- return new List<Expr> { expr };
- }
- var fun = nary.Fun as BinaryOperator;
- if(fun == null || (fun.Op != BinaryOperator.Opcode.And)) {
- return new List<Expr> { expr };
- }
- var result = new List<Expr>();
- result.AddRange(BreakIntoConjuncts(nary.Args[0]));
- result.AddRange(BreakIntoConjuncts(nary.Args[1]));
- return result;
- }
-
- private bool NoStages() {
- return plan == null;
- }
-
- public HoudiniOutcome PerformStagedHoudiniInference()
- {
-
- if (NoStages()) {
- Houdini houdini = new Houdini(program, houdiniStats);
- return houdini.PerformHoudiniInference();
- }
-
- EmitProgram(tempFilename);
-
- #region Prepare the tasks, but do not launch them
- foreach (var s in plan) {
- Debug.Assert(!plan.GetDependences(s).Contains(s));
- tasks.Add(new StagedHoudiniTask(s, new Task(o => {
- ExecuteStage((ScheduledStage)o);
- }, s, TaskCreationOptions.LongRunning)));
- }
- #endregion
-
- #region Launch the tasks, and wait for them to finish
- foreach (var t in tasks) {
- t.parallelTask.Start();
- }
- Task.WaitAll(tasks.Select(Item => Item.parallelTask).ToArray());
- int count = 0;
- foreach(var h in houdiniInstances) {
- if(h.Count() > 0) {
- count++;
- System.Diagnostics.Debug.Assert(h.Count() == 1);
- h[0].Close();
- }
- }
- #endregion
-
- return UnifyOutcomes();
-
- }
-
- private HoudiniOutcome UnifyOutcomes()
- {
- HoudiniOutcome result = new HoudiniOutcome();
- var scheduledStages = outcomes.Keys.ToList();
-
- result.assignment = new Dictionary<string,bool>();
- foreach(var c in outcomes[scheduledStages[0]].assignment.Keys) {
- result.assignment[c] = outcomes.Select(Item => Item.Value).Select(Item => Item.assignment[c]).All(Item => Item);
- }
-
- result.implementationOutcomes = new Dictionary<string,VCGenOutcome>();
- foreach(var p in outcomes[scheduledStages[0]].implementationOutcomes.Keys) {
- var unifiedImplementationOutcome = outcomes[scheduledStages[0]].implementationOutcomes[p];
- for(int i = 1; i < scheduledStages.Count(); i++) {
- unifiedImplementationOutcome = ChooseOutcome(unifiedImplementationOutcome,
- outcomes[scheduledStages[i]].implementationOutcomes[p]);
- }
- result.implementationOutcomes[p] = unifiedImplementationOutcome;
- }
-
- return result;
- }
-
- private void ExecuteStage(ScheduledStage s)
- {
- Task.WaitAll(tasks.Where(
- Item => plan.GetDependences(s).Contains(Item.stage)).
- Select(Item => Item.parallelTask).ToArray());
-
- if(s.Count() == 0) {
- // This is the trivial first stage, so don't launch Houdini;
- // give this a null outcome
- return;
- }
-
- List<Houdini> h = AcquireHoudiniInstance();
-
- if (h.Count() == 0)
- {
- h.Add(new Houdini(ProgramFromFile(tempFilename), new HoudiniSession.HoudiniStatistics(), "houdiniCexTrace_" + s.GetId() + ".txt"));
- }
-
- System.Diagnostics.Debug.Assert(h.Count() == 1);
-
- Dictionary<string, bool> mergedAssignment = null;
-
- List<Dictionary<string, bool>> relevantAssignments;
- IEnumerable<int> completedStages;
- lock (outcomes)
- {
- relevantAssignments =
- outcomes.Where(Item => plan.Contains(Item.Key)).
- Select(Item => Item.Value).
- Select(Item => Item.assignment).ToList();
- completedStages = plan.GetDependences(s).Select(Item => Item.GetId());
- }
-
- if (relevantAssignments.Count() > 0)
- {
- mergedAssignment = new Dictionary<string, bool>();
- foreach (var v in relevantAssignments[0].Keys)
- {
- mergedAssignment[v] = relevantAssignments.Select(Item => Item[v]).ToList().All(Item => Item);
- }
- }
-
- HoudiniOutcome outcome = h[0].PerformHoudiniInference(
- s.GetId(),
- completedStages,
- mergedAssignment);
-
- lock (outcomes)
- {
- outcomes[s] = outcome;
- }
-
- ReleaseHoudiniInstance(h);
-
- }
-
- private static void ReleaseHoudiniInstance(List<Houdini> h)
- {
- Monitor.Exit(h);
- }
-
- private List<Houdini> AcquireHoudiniInstance()
- {
- while(true) {
- foreach (var houdini in houdiniInstances) {
- if (Monitor.TryEnter(houdini)) {
- return houdini;
- }
- Thread.Sleep(20);
- }
- }
- }
-
- private void EmitProgram(string filename)
- {
- using (TokenTextWriter writer = new TokenTextWriter(filename, true))
- {
- int oldPrintUnstructured = CommandLineOptions.Clo.PrintUnstructured;
- CommandLineOptions.Clo.PrintUnstructured = 2;
- program.Emit(writer);
- CommandLineOptions.Clo.PrintUnstructured = oldPrintUnstructured;
- }
- }
-
-
- private static VCGenOutcome ChooseOutcome(VCGenOutcome o1, VCGenOutcome o2) {
- var vcOutcome1 = o1.outcome;
- var vcOutcome2 = o2.outcome;
-
- if(vcOutcome1 == vcOutcome2) {
- return o1;
- }
-
- // Errors trump everything else
- if(vcOutcome1 == VC.ConditionGeneration.Outcome.Errors) {
- return o1;
- }
- if(vcOutcome2 == VC.ConditionGeneration.Outcome.Errors) {
- return o2;
- }
-
- // If one outcome is Correct, return the other in case it is "worse"
- if(vcOutcome1 == VC.ConditionGeneration.Outcome.Correct) {
- return o2;
- }
- if(vcOutcome2 == VC.ConditionGeneration.Outcome.Correct) {
- return o1;
- }
-
- // Neither outcome is correct, so if one outcome is ReachedBound, return the other in case it is "worse"
- if(vcOutcome1 == VC.ConditionGeneration.Outcome.ReachedBound) {
- return o2;
- }
- if(vcOutcome2 == VC.ConditionGeneration.Outcome.ReachedBound) {
- return o1;
- }
-
- // Both outcomes must be timeout or memout; arbitrarily choose the first
- return o1;
- }
-
- internal class StagedHoudiniTask {
- internal ScheduledStage stage;
- internal Task parallelTask;
- internal StagedHoudiniTask(ScheduledStage stage, Task parallelTask) {
- this.stage = stage;
- this.parallelTask = parallelTask;
- }
- }
-
- }
-
- public class StagedHoudiniPlan : IEnumerable<ScheduledStage> {
-
- private Graph<ScheduledStage> ScheduledStages;
- private Dictionary<string, ScheduledStage> AnnotationToStage;
-
- internal StagedHoudiniPlan(Graph<ScheduledStage> ScheduledStages) {
- this.ScheduledStages = ScheduledStages;
- this.AnnotationToStage = new Dictionary<string, ScheduledStage>();
- foreach(var s in this) {
- Debug.Assert(!GetDependences(s).Contains(s));
- }
- }
-
- public IEnumerable<ScheduledStage> GetDependences(ScheduledStage s) {
- IEnumerable<ScheduledStage> result;
- lock(ScheduledStages) {
- result = ScheduledStages.Successors(s);
- }
- return result;
- }
-
-
- private static int CompareStages(ScheduledStage s1, ScheduledStage s2) {
- if(s1.GetId() < s2.GetId()) {
- return -1;
- }
- if(s2.GetId() < s1.GetId()) {
- return 1;
- }
- return 0;
- }
-
- public IEnumerator<ScheduledStage> GetEnumerator() {
- List<ScheduledStage> sortedStages = ScheduledStages.Nodes.ToList();
- sortedStages.Sort(CompareStages);
- return sortedStages.GetEnumerator();
- }
-
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
- {
- return this.GetEnumerator();
- }
-
- internal ScheduledStage StageForAnnotation(string c) {
- if(AnnotationToStage.ContainsKey(c)) {
- return AnnotationToStage[c];
- }
- foreach(var s in ScheduledStages.Nodes) {
- if(s.ContainsAnnotation(c)) {
- AnnotationToStage[c] = s;
- return s;
- }
- }
- return null;
- }
-
- public override string ToString()
- {
- string result = "";
- foreach(ScheduledStage s in this) {
- result += "Stage " + s;
-
- result += " depends on stages: ";
- foreach(var id in GetDependences(s).Select(Item => Item.GetId())) {
- result += id + " ";
- }
- result += "\n";
- }
- return result;
- }
- }
-
- public class ScheduledStage {
- private int Id;
- private HashSet<string> Annotations;
-
- public ScheduledStage(int Id, HashSet<string> Annotations) {
- this.Id = Id;
- this.Annotations = Annotations;
- }
-
- internal void AddAnnotation(string a) {
- Annotations.Add(a);
- }
-
- internal bool ContainsAnnotation(string a) {
- return Annotations.Contains(a);
- }
-
- public int GetId() {
- return Id;
- }
-
- public int Count() {
- return Annotations.Count();
- }
-
- public override string ToString()
- {
- string result = "ID: " + Id + "{ ";
- foreach(var c in Annotations) {
- result += c + " ";
- }
- result += "}\n";
- return result;
- }
- }
-
-
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Diagnostics.Contracts;
+using System.Diagnostics;
+using System.Threading.Tasks;
+using System.Threading;
+using Microsoft.Boogie;
+using Microsoft.Boogie.GraphUtil;
+
+namespace Microsoft.Boogie.Houdini
+{
+ public class StagedHoudini
+ {
+
+ private Program program;
+ private HoudiniSession.HoudiniStatistics houdiniStats;
+ private Func<string, Program> ProgramFromFile;
+ private StagedHoudiniPlan plan;
+ private List<Houdini>[] houdiniInstances;
+ private List<StagedHoudiniTask> tasks = new List<StagedHoudiniTask>();
+ private Dictionary<ScheduledStage, HoudiniOutcome> outcomes = new Dictionary<ScheduledStage,HoudiniOutcome>();
+
+ private const string tempFilename = "__stagedHoudiniTemp.bpl";
+
+ public StagedHoudini(Program program, HoudiniSession.HoudiniStatistics houdiniStats, Func<string, Program> ProgramFromFile) {
+ this.program = program;
+ this.houdiniStats = houdiniStats;
+ this.ProgramFromFile = ProgramFromFile;
+ this.houdiniInstances = new List<Houdini>[CommandLineOptions.Clo.StagedHoudiniThreads];
+ for (int i = 0; i < CommandLineOptions.Clo.StagedHoudiniThreads; i++) {
+ houdiniInstances[i] = new List<Houdini>();
+ }
+
+ BreakApartConjunctionsInAnnotations();
+
+ var annotationDependenceAnalyser = new AnnotationDependenceAnalyser(program);
+ annotationDependenceAnalyser.Analyse();
+ this.plan = annotationDependenceAnalyser.ApplyStages();
+ if (CommandLineOptions.Clo.Trace)
+ {
+ annotationDependenceAnalyser.dump();
+
+ if(CommandLineOptions.Clo.DebugStagedHoudini) {
+ Console.WriteLine("Plan\n====\n");
+ if(plan == null) {
+ Console.WriteLine("No plan, as there were no stages");
+ } else {
+ Console.WriteLine(this.plan);
+ }
+ }
+
+ EmitProgram("staged.bpl");
+ }
+ }
+
+ private void BreakApartConjunctionsInAnnotations()
+ {
+ // StagedHoudini works on a syntactic basis, so that
+ // if x and y occur in the same annotation, any annotation
+ // referring to either x or y will be in the same stage
+ // as this annotation. It is thus desirable to separate
+ // conjunctive annotations into multiple annotations,
+ // to reduce these syntactic dependencies.
+
+ foreach(var b in program.Blocks()) {
+ List<Cmd> newCmds = new List<Cmd>();
+ foreach(var c in b.Cmds) {
+ var assertion = c as AssertCmd;
+ if (assertion != null) {
+ foreach(var e in BreakIntoConjuncts(assertion.Expr)) {
+ newCmds.Add(new AssertCmd(assertion.tok, e, assertion.Attributes));
+ }
+ } else {
+ newCmds.Add(c);
+ }
+ }
+ b.Cmds = newCmds;
+ }
+
+ foreach(var proc in program.Procedures) {
+ {
+ var newRequires = new List<Requires>();
+ foreach(var r in proc.Requires) {
+ foreach(var c in BreakIntoConjuncts(r.Condition)) {
+ newRequires.Add(new Requires(r.tok, r.Free, c, r.Comment, r.Attributes));
+ }
+ }
+ proc.Requires = newRequires;
+ }
+ {
+ var newEnsures = new List<Ensures>();
+ foreach(var e in proc.Ensures) {
+ foreach(var c in BreakIntoConjuncts(e.Condition)) {
+ newEnsures.Add(new Ensures(e.tok, e.Free, c, e.Comment, e.Attributes));
+ }
+ }
+ proc.Ensures = newEnsures;
+ }
+ }
+ }
+
+ private List<Expr> BreakIntoConjuncts(Expr expr)
+ {
+ var nary = expr as NAryExpr;
+ if(nary == null) {
+ return new List<Expr> { expr };
+ }
+ var fun = nary.Fun as BinaryOperator;
+ if(fun == null || (fun.Op != BinaryOperator.Opcode.And)) {
+ return new List<Expr> { expr };
+ }
+ var result = new List<Expr>();
+ result.AddRange(BreakIntoConjuncts(nary.Args[0]));
+ result.AddRange(BreakIntoConjuncts(nary.Args[1]));
+ return result;
+ }
+
+ private bool NoStages() {
+ return plan == null;
+ }
+
+ public HoudiniOutcome PerformStagedHoudiniInference()
+ {
+
+ if (NoStages()) {
+ Houdini houdini = new Houdini(program, houdiniStats);
+ return houdini.PerformHoudiniInference();
+ }
+
+ EmitProgram(tempFilename);
+
+ #region Prepare the tasks, but do not launch them
+ foreach (var s in plan) {
+ Debug.Assert(!plan.GetDependences(s).Contains(s));
+ tasks.Add(new StagedHoudiniTask(s, new Task(o => {
+ ExecuteStage((ScheduledStage)o);
+ }, s, TaskCreationOptions.LongRunning)));
+ }
+ #endregion
+
+ #region Launch the tasks, and wait for them to finish
+ foreach (var t in tasks) {
+ t.parallelTask.Start();
+ }
+ Task.WaitAll(tasks.Select(Item => Item.parallelTask).ToArray());
+ int count = 0;
+ foreach(var h in houdiniInstances) {
+ if(h.Count() > 0) {
+ count++;
+ System.Diagnostics.Debug.Assert(h.Count() == 1);
+ h[0].Close();
+ }
+ }
+ #endregion
+
+ return UnifyOutcomes();
+
+ }
+
+ private HoudiniOutcome UnifyOutcomes()
+ {
+ HoudiniOutcome result = new HoudiniOutcome();
+ var scheduledStages = outcomes.Keys.ToList();
+
+ result.assignment = new Dictionary<string,bool>();
+ foreach(var c in outcomes[scheduledStages[0]].assignment.Keys) {
+ result.assignment[c] = outcomes.Select(Item => Item.Value).Select(Item => Item.assignment[c]).All(Item => Item);
+ }
+
+ result.implementationOutcomes = new Dictionary<string,VCGenOutcome>();
+ foreach(var p in outcomes[scheduledStages[0]].implementationOutcomes.Keys) {
+ var unifiedImplementationOutcome = outcomes[scheduledStages[0]].implementationOutcomes[p];
+ for(int i = 1; i < scheduledStages.Count(); i++) {
+ unifiedImplementationOutcome = ChooseOutcome(unifiedImplementationOutcome,
+ outcomes[scheduledStages[i]].implementationOutcomes[p]);
+ }
+ result.implementationOutcomes[p] = unifiedImplementationOutcome;
+ }
+
+ return result;
+ }
+
+ private void ExecuteStage(ScheduledStage s)
+ {
+ Task.WaitAll(tasks.Where(
+ Item => plan.GetDependences(s).Contains(Item.stage)).
+ Select(Item => Item.parallelTask).ToArray());
+
+ if(s.Count() == 0) {
+ // This is the trivial first stage, so don't launch Houdini;
+ // give this a null outcome
+ return;
+ }
+
+ List<Houdini> h = AcquireHoudiniInstance();
+
+ if (h.Count() == 0)
+ {
+ h.Add(new Houdini(ProgramFromFile(tempFilename), new HoudiniSession.HoudiniStatistics(), "houdiniCexTrace_" + s.GetId() + ".txt"));
+ }
+
+ System.Diagnostics.Debug.Assert(h.Count() == 1);
+
+ Dictionary<string, bool> mergedAssignment = null;
+
+ List<Dictionary<string, bool>> relevantAssignments;
+ IEnumerable<int> completedStages;
+ lock (outcomes)
+ {
+ relevantAssignments =
+ outcomes.Where(Item => plan.Contains(Item.Key)).
+ Select(Item => Item.Value).
+ Select(Item => Item.assignment).ToList();
+ completedStages = plan.GetDependences(s).Select(Item => Item.GetId());
+ }
+
+ if (relevantAssignments.Count() > 0)
+ {
+ mergedAssignment = new Dictionary<string, bool>();
+ foreach (var v in relevantAssignments[0].Keys)
+ {
+ mergedAssignment[v] = relevantAssignments.Select(Item => Item[v]).ToList().All(Item => Item);
+ }
+ }
+
+ HoudiniOutcome outcome = h[0].PerformHoudiniInference(
+ s.GetId(),
+ completedStages,
+ mergedAssignment);
+
+ lock (outcomes)
+ {
+ outcomes[s] = outcome;
+ }
+
+ ReleaseHoudiniInstance(h);
+
+ }
+
+ private static void ReleaseHoudiniInstance(List<Houdini> h)
+ {
+ Monitor.Exit(h);
+ }
+
+ private List<Houdini> AcquireHoudiniInstance()
+ {
+ while(true) {
+ foreach (var houdini in houdiniInstances) {
+ if (Monitor.TryEnter(houdini)) {
+ return houdini;
+ }
+ Thread.Sleep(20);
+ }
+ }
+ }
+
+ private void EmitProgram(string filename)
+ {
+ using (TokenTextWriter writer = new TokenTextWriter(filename, true))
+ {
+ int oldPrintUnstructured = CommandLineOptions.Clo.PrintUnstructured;
+ CommandLineOptions.Clo.PrintUnstructured = 2;
+ program.Emit(writer);
+ CommandLineOptions.Clo.PrintUnstructured = oldPrintUnstructured;
+ }
+ }
+
+
+ private static VCGenOutcome ChooseOutcome(VCGenOutcome o1, VCGenOutcome o2) {
+ var vcOutcome1 = o1.outcome;
+ var vcOutcome2 = o2.outcome;
+
+ if(vcOutcome1 == vcOutcome2) {
+ return o1;
+ }
+
+ // Errors trump everything else
+ if(vcOutcome1 == VC.ConditionGeneration.Outcome.Errors) {
+ return o1;
+ }
+ if(vcOutcome2 == VC.ConditionGeneration.Outcome.Errors) {
+ return o2;
+ }
+
+ // If one outcome is Correct, return the other in case it is "worse"
+ if(vcOutcome1 == VC.ConditionGeneration.Outcome.Correct) {
+ return o2;
+ }
+ if(vcOutcome2 == VC.ConditionGeneration.Outcome.Correct) {
+ return o1;
+ }
+
+ // Neither outcome is correct, so if one outcome is ReachedBound, return the other in case it is "worse"
+ if(vcOutcome1 == VC.ConditionGeneration.Outcome.ReachedBound) {
+ return o2;
+ }
+ if(vcOutcome2 == VC.ConditionGeneration.Outcome.ReachedBound) {
+ return o1;
+ }
+
+ // Both outcomes must be timeout or memout; arbitrarily choose the first
+ return o1;
+ }
+
+ internal class StagedHoudiniTask {
+ internal ScheduledStage stage;
+ internal Task parallelTask;
+ internal StagedHoudiniTask(ScheduledStage stage, Task parallelTask) {
+ this.stage = stage;
+ this.parallelTask = parallelTask;
+ }
+ }
+
+ }
+
+ public class StagedHoudiniPlan : IEnumerable<ScheduledStage> {
+
+ private Graph<ScheduledStage> ScheduledStages;
+ private Dictionary<string, ScheduledStage> AnnotationToStage;
+
+ internal StagedHoudiniPlan(Graph<ScheduledStage> ScheduledStages) {
+ this.ScheduledStages = ScheduledStages;
+ this.AnnotationToStage = new Dictionary<string, ScheduledStage>();
+ foreach(var s in this) {
+ Debug.Assert(!GetDependences(s).Contains(s));
+ }
+ }
+
+ public IEnumerable<ScheduledStage> GetDependences(ScheduledStage s) {
+ IEnumerable<ScheduledStage> result;
+ lock(ScheduledStages) {
+ result = ScheduledStages.Successors(s);
+ }
+ return result;
+ }
+
+
+ private static int CompareStages(ScheduledStage s1, ScheduledStage s2) {
+ if(s1.GetId() < s2.GetId()) {
+ return -1;
+ }
+ if(s2.GetId() < s1.GetId()) {
+ return 1;
+ }
+ return 0;
+ }
+
+ public IEnumerator<ScheduledStage> GetEnumerator() {
+ List<ScheduledStage> sortedStages = ScheduledStages.Nodes.ToList();
+ sortedStages.Sort(CompareStages);
+ return sortedStages.GetEnumerator();
+ }
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+ {
+ return this.GetEnumerator();
+ }
+
+ internal ScheduledStage StageForAnnotation(string c) {
+ if(AnnotationToStage.ContainsKey(c)) {
+ return AnnotationToStage[c];
+ }
+ foreach(var s in ScheduledStages.Nodes) {
+ if(s.ContainsAnnotation(c)) {
+ AnnotationToStage[c] = s;
+ return s;
+ }
+ }
+ return null;
+ }
+
+ public override string ToString()
+ {
+ string result = "";
+ foreach(ScheduledStage s in this) {
+ result += "Stage " + s;
+
+ result += " depends on stages: ";
+ foreach(var id in GetDependences(s).Select(Item => Item.GetId())) {
+ result += id + " ";
+ }
+ result += "\n";
+ }
+ return result;
+ }
+ }
+
+ public class ScheduledStage {
+ private int Id;
+ private HashSet<string> Annotations;
+
+ public ScheduledStage(int Id, HashSet<string> Annotations) {
+ this.Id = Id;
+ this.Annotations = Annotations;
+ }
+
+ internal void AddAnnotation(string a) {
+ Annotations.Add(a);
+ }
+
+ internal bool ContainsAnnotation(string a) {
+ return Annotations.Contains(a);
+ }
+
+ public int GetId() {
+ return Id;
+ }
+
+ public int Count() {
+ return Annotations.Count();
+ }
+
+ public override string ToString()
+ {
+ string result = "ID: " + Id + "{ ";
+ foreach(var c in Annotations) {
+ result += c + " ";
+ }
+ result += "}\n";
+ return result;
+ }
+ }
+
+
+}