public static HashSet <string> RunHoudini(Program program, bool RobustAgainstEvaluate = false) { HoudiniStats.Reset(); HoudiniInlining.RobustAgainstEvaluate = DualHoudini? false : RobustAgainstEvaluate; if (DualHoudini && CommandLineOptions.Clo.InlineDepth > 0) { throw new DualHoudiniFail("InlineDepth not supported"); } // Gather existential constants var CandidateConstants = new Dictionary <string, Constant>(); program.TopLevelDeclarations.OfType <Constant>() .Where(c => QKeyValue.FindBoolAttribute(c.Attributes, "existential")) .Iter(c => CandidateConstants.Add(c.Name, c)); // Create a function, one for each impl, for book-keeping var CandidateFuncsAssumed = new Dictionary <string, Function>(); var CandidateFuncsAsserted = new Dictionary <string, Function>(); var AssumeToAssert = new Dictionary <Function, Function>(); program.TopLevelDeclarations.OfType <Implementation>() .Iter(impl => { var fassumed = GetCandidateFunc(CandidateFuncPrefix, impl.Name); var fasserted = GetCandidateFunc(CandidateFuncAssertedPrefix, impl.Name); CandidateFuncsAssumed.Add(impl.Name, fassumed); CandidateFuncsAsserted.Add(impl.Name, fasserted); AssumeToAssert.Add(fassumed, fasserted); }); // Tag the ensures so we can keep track of them var iterimpls = program.TopLevelDeclarations.OfType <Implementation>().ToList(); iterimpls.Iter(impl => InstrumentEnsures(program, impl, CandidateFuncsAssumed[impl.Name], CandidateConstants)); //BoogieUtil.PrintProgram(program, "h2.bpl"); var RewriteAssumedToAssertedAction = new Action <Implementation>(impl => { // Rewrite functions that are asserted var rewrite = new RewriteFuncs(AssumeToAssert); foreach (var blk in impl.Blocks) { foreach (var acmd in blk.Cmds.OfType <AssertCmd>()) { acmd.Expr = rewrite.VisitExpr(acmd.Expr); } } var funcs = new HashSet <Function>(CandidateFuncsAssumed.Values); // Move call-site constant to the first argument of CandidateFuncs foreach (var blk in impl.Blocks) { Expr cv = null; // walk backwards for (int i = blk.Cmds.Count - 1; i >= 0; i--) { var acmd = blk.Cmds[i] as AssumeCmd; if (acmd == null) { continue; } if (QKeyValue.FindBoolAttribute(acmd.Attributes, StratifiedVCGenBase.callSiteVarAttr)) { cv = acmd.Expr; continue; } InsertControlVar.Apply(acmd.Expr, funcs, cv); } } //impl.Emit(new TokenTextWriter(Console.Out), 0); }); program.AddTopLevelDeclarations(CandidateFuncsAssumed.Values); program.AddTopLevelDeclarations(CandidateFuncsAsserted.Values); var callgraph = BoogieUtil.GetCallGraph(program); var impl2Priority = DeterminePriorityOrder(program, callgraph); var impls = new HashSet <string>(impl2Priority.Keys); HoudiniStats.Start("VCGen"); // VC Gen var hi = new HoudiniInlining(program, CommandLineOptions.Clo.ProverLogFilePath, CommandLineOptions.Clo.ProverLogFileAppend, RewriteAssumedToAssertedAction); HoudiniStats.Stop("VCGen"); var worklist = new SortedSet <Tuple <int, string> >(); impl2Priority.Iter(tup => worklist.Add(Tuple.Create(tup.Value, tup.Key))); // Current assignment: set of true constants // Initially: everything is true var assignment = new HashSet <string>(CandidateConstants.Keys); var prover = hi.prover; var reporter = new EmptyErrorReporter(); // assert true to flush all one-time axioms, decls, etc prover.Assert(VCExpressionGenerator.True, true); HoudiniStats.Start("MainLoop"); // worklist algorithm while (worklist.Any()) { var implName = worklist.First().Item2; worklist.Remove(worklist.First()); if (dbg) { Console.WriteLine("Processing " + implName); } prover.LogComment("Processing " + implName); prover.Push(); var hvc = new HoudiniVC(hi.implName2StratifiedInliningInfo[implName], impls, assignment); var openCallSites = new HashSet <StratifiedCallSite>(hvc.CallSites); prover.Assert(hvc.vcexpr, true); var candidates = !DualHoudini ? new HashSet <string>(hvc.constantToAssertedExpr.Keys.Where(k => assignment.Contains(k))) : new HashSet <string>(hvc.constantToAssumedExpr.Select(t => t.Item1).Where(k => assignment.Contains(k))); var provedTrue = new HashSet <string>(); var provedFalse = new HashSet <string>(); var idepth = Math.Max(0, CommandLineOptions.Clo.InlineDepth); // iterate over idepth while (true) { // Part 1: over-approximate var proved = ProveCandidates(prover, hvc.constantToAssertedExpr, hvc.constantToAssumedExpr, candidates.Difference(provedTrue.Union(provedFalse))); provedTrue.UnionWith(proved); if (dbg) { Console.WriteLine("Proved {0} candiates at depth {1}", proved.Count, CommandLineOptions.Clo.InlineDepth - idepth); } if (idepth == 0 || openCallSites.Count == 0) { break; } // Part 2: under-approximate prover.Push(); foreach (var cs in openCallSites) { prover.Assert(cs.callSiteExpr, false); } var remaining = candidates.Difference(provedTrue.Union(provedFalse)); proved = ProveCandidates(prover, hvc.constantToAssertedExpr, hvc.constantToAssumedExpr, remaining); provedFalse.UnionWith(remaining.Difference(proved)); if (dbg) { Console.WriteLine("Disproved {0} candiates at depth {1}", remaining.Difference(proved).Count, CommandLineOptions.Clo.InlineDepth - idepth); } prover.Pop(); // resolved all? if (candidates.Difference(provedTrue.Union(provedFalse)).Count == 0) { break; } // Inline one level idepth--; var nextOpenCallSites = new HashSet <StratifiedCallSite>(); foreach (var cs in openCallSites) { var callee = new HoudiniVC(hi.implName2StratifiedInliningInfo[cs.callSite.calleeName], impls, assignment); var calleevc = cs.Attach(callee); prover.Assert(prover.VCExprGen.Implies(cs.callSiteExpr, calleevc), true); nextOpenCallSites.UnionWith(callee.CallSites); } openCallSites = nextOpenCallSites; } prover.Pop(); var failed = candidates.Difference(provedTrue); assignment.ExceptWith(failed); if (failed.Count != 0) { // add dependencies back into the worklist if (!DualHoudini) { foreach (var caller in callgraph.Predecessors(implName)) { worklist.Add(Tuple.Create(impl2Priority[caller], caller)); } } else { foreach (var caller in callgraph.Successors(implName)) { worklist.Add(Tuple.Create(impl2Priority[caller], caller)); } } } } HoudiniStats.Stop("MainLoop"); hi.Close(); return(assignment); }
// Prove candidates that hold (using an over-approximation, at the current inline depth). // Return the set of candidates proved correct static HashSet <string> ProveCandidates(ProverInterface prover, Dictionary <string, VCExpr> constantToAssertedExpr, List <Tuple <string, VCExpr, VCExpr> > constantToAssumedExpr, HashSet <string> candidates) { var remaining = new HashSet <string>(candidates); var reporter = new EmptyErrorReporter(); var failed = new HashSet <string>(); // for dual houdini, we have to iterate once around to the loop // even if remaining is empty bool secondtry = false; while (true) { remaining.ExceptWith(failed); if (remaining.Count == 0 && (!DualHoudini || secondtry)) { break; } if (remaining.Count == 0) { secondtry = true; } else { secondtry = false; } HoudiniStats.ProverCount++; HoudiniStats.Start("ProverTime"); prover.Push(); var nameToCallSiteVar = new Dictionary <string, VCExprVar>(); var callSiteVarToConstantToExpr = new Dictionary <string, Dictionary <string, VCExpr> >(); if (!DualHoudini) { // assert post VCExpr toassert = VCExpressionGenerator.True; foreach (var k in remaining) { toassert = prover.VCExprGen.And(toassert, constantToAssertedExpr[k]); } prover.Assert(toassert, false); } else { // assume post of callees foreach (var tup in constantToAssumedExpr) { if (!remaining.Contains(tup.Item1)) { continue; } var csVar = tup.Item2 as VCExprVar; Debug.Assert(csVar != null); if (!nameToCallSiteVar.ContainsKey(csVar.ToString())) { nameToCallSiteVar.Add(csVar.ToString(), csVar); } if (!callSiteVarToConstantToExpr.ContainsKey(csVar.ToString())) { callSiteVarToConstantToExpr.Add(csVar.ToString(), new Dictionary <string, VCExpr>()); } callSiteVarToConstantToExpr[csVar.ToString()].Add(tup.Item1, tup.Item3); } foreach (var tup in callSiteVarToConstantToExpr) { var expr = VCExpressionGenerator.False; tup.Value.Values.Iter(e => expr = prover.VCExprGen.Or(expr, e)); prover.Assert(prover.VCExprGen.Implies(nameToCallSiteVar[tup.Key], expr), true); } } Dictionary <string, VCExprVar> recordingBool = null; if (RobustAgainstEvaluate) { recordingBool = new Dictionary <string, VCExprVar>(); VCExpr torecord = VCExpressionGenerator.True; foreach (var k in remaining) { var b = prover.VCExprGen.Variable("recordingVar_" + k, Microsoft.Boogie.Type.Bool); recordingBool.Add(k, b); torecord = prover.VCExprGen.And(torecord, prover.VCExprGen.Eq(b, constantToAssertedExpr[k])); } prover.Assert(torecord, true); } prover.Check(); var outcome = prover.CheckOutcomeCore(reporter); // check which ones failed if (outcome == ProverInterface.Outcome.Invalid || outcome == ProverInterface.Outcome.Undetermined) { var removed = 0; if (!DualHoudini) { foreach (var k in remaining) { var b = recordingBool == null ? (bool)prover.Evaluate(constantToAssertedExpr[k]) : (bool)prover.Evaluate(recordingBool[k]); if (!b) { failed.Add(k); if (dbg) { Console.WriteLine("Failed: {0}", k); } removed++; } } } else { foreach (var tup in callSiteVarToConstantToExpr) { if (!(bool)prover.Evaluate(nameToCallSiteVar[tup.Key])) { continue; } // call site taken foreach (var tup2 in tup.Value) { if ((bool)prover.Evaluate(tup2.Value)) { failed.Add(tup2.Key); if (dbg) { Console.WriteLine("Failed: {0}", tup2.Key); } removed++; } } } if (removed == 0) { throw new DualHoudiniFail("Nothing to drop"); } } Debug.Assert(removed != 0); //if(dbg) Console.WriteLine("Query {0}: Invalid, {1} removed", HoudiniStats.ProverCount, removed); } HoudiniStats.Stop("ProverTime"); prover.Pop(); if (outcome == ProverInterface.Outcome.TimeOut || outcome == ProverInterface.Outcome.OutOfMemory) { if (DualHoudini) { throw new DualHoudiniFail("Timeout"); } failed.UnionWith(remaining); break; } if (outcome == ProverInterface.Outcome.Valid) { //if(dbg) Console.WriteLine("Query {0}: Valid", HoudiniStats.ProverCount); break; } } remaining.ExceptWith(failed); return(remaining); }