static void PrintCallGraphToDot(Program program, string root, string filename) { var dotty = new System.IO.StreamWriter(filename); var graph = BoogieUtil.GetCallGraph(program); if (!program.Implementations.Any(impl => impl.Proc.Name.Equals(root))) { Console.WriteLine("Warning: specified root is not found"); Console.WriteLine("Get all non-trivial roots:"); // ignore recursion var roots = graph.Nodes.Where(n => graph.Predecessors(n).Count() == 0 && graph.Successors(n).Count() > 0); Console.WriteLine(String.Join(", ", roots.ToArray())); Console.WriteLine("Print the whole graph with all roots"); dotty.Write(graph.ToDot()); dotty.Close(); return; } dotty.WriteLine("digraph \"Call graph\" {"); dotty.WriteLine(" label=\"Call graph\";"); var visited = new HashSet <string> { root }; var worklist = new HashSet <string> { root }; var depth = 0; // do BFS while (worklist.Count != 0 && depth < 50) { depth++; // print all edges from this level to next worklist.Where(w => !Regex.IsMatch(w, @"^devirtbounce\d*$")).Iter(w => graph.Successors(w).Where(q => !Regex.IsMatch(q, @"^devirtbounce\d*$")).Iter(s => dotty.WriteLine(string.Format(" \"{0}\" -> \"{1}\";", w, s)))); // expand worklist to next level var nl = new HashSet <string>(); worklist.Iter(w => nl.UnionWith(graph.Successors(w).Where(x => !Regex.IsMatch(x, @"^devirtbounce\d*$")))); // break circle worklist = nl.Difference(visited); // add next level to visited visited.UnionWith(nl); } dotty.WriteLine("}"); dotty.Close(); }
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); }