/// <summary> /// Apply the assignments in the form of axioms. /// </summary> /// <param name="program">The program.</param> /// <param name="assignments">The assignments provided by the solver.</param> private void ApplyAssignments(Microsoft.Boogie.Program program, Dictionary <string, bool> assignments) { // remove existing axioms List <Axiom> axioms = new List <Axiom>(); foreach (Axiom axiom in program.Axioms.Where(x => x.Comment == "repair_constraint")) { axioms.Add(axiom); } axioms.ForEach(x => program.RemoveTopLevelDeclaration(x)); // add the axioms corresponding to the assignments foreach (KeyValuePair <string, bool> assignment in assignments) { Expr identifier = new IdentifierExpr(Token.NoToken, assignment.Key, Type.Bool); Expr literal = new LiteralExpr(Token.NoToken, assignment.Value); BinaryOperator _operator = new BinaryOperator(Token.NoToken, BinaryOperator.Opcode.Iff); NAryExpr expr = new NAryExpr(Token.NoToken, _operator, new List <Expr> { identifier, literal }); Axiom axiom = new Axiom(Token.NoToken, expr, "repair_constraint"); program.AddTopLevelDeclaration(axiom); } }
public bpl.Program split(bpl.Program prog) { originalProgram = prog; ////////////////// First identify the thread entries in the program //////////////////// // A procedure is a thread entry if there is any async call to it HashSet <string> threadEntries = new HashSet <string>(); var impls = new Func <bpl.Program, IEnumerable <bpl.Implementation> >(p => from impl in p.TopLevelDeclarations where impl is bpl.Implementation select impl as bpl.Implementation); foreach (var impl in impls(originalProgram)) { var cmdsRaw = from blk in impl.Blocks select blk.Cmds; var cmds = cmdsRaw.Aggregate(new List <bpl.Cmd>(), (curCmds, nextList) => { curCmds.AddRange(nextList); return(curCmds); }); var asyncCalls = from cmd in cmds where con.isAsyncCall(cmd as bpl.Cmd) select cmd; foreach (bpl.Cmd asyncCall in asyncCalls) { threadEntries.Add((asyncCall as bpl.CallCmd).callee); } } // Finally add the entry function also as a thread entry. threadEntries.Add(con.entryFunc); //////////////// Next, identify all the procedures belonging to each thread //////////// Dictionary <string, HashSet <string> > threadToProcs = new Dictionary <string, HashSet <string> >(); // This is done in two steps. // First step: Find all non-async calls in each procedure. var procToCalls = new Dictionary <string, HashSet <string> >(); //impls = from impl in prog.TopLevelDeclarations where impl is bpl.Implementation select impl as bpl.Implementation; foreach (var impl in impls(originalProgram)) { procToCalls[impl.Name] = new HashSet <string>(); var cmdsRaw = from blk in impl.Blocks select blk.Cmds; var cmds = cmdsRaw.Aggregate(new List <bpl.Cmd>(), (curCmds, nextList) => { curCmds.AddRange(nextList); return(curCmds); }); var syncCalls = from cmd in cmds where con.isSyncCall(cmd as bpl.Cmd) select cmd; foreach (bpl.Cmd syncCall in syncCalls) { procToCalls[impl.Name].Add((syncCall as bpl.CallCmd).callee); } } // Second step: Find all procedures in each thread. foreach (var thr in threadEntries) { threadToProcs[thr] = new HashSet <string>(); } foreach (var thr in threadEntries) { findReachable(procToCalls, thr, threadToProcs[thr]); } //DEBUGGING if (dbg) { System.Console.WriteLine("Threads in the original program:"); foreach (var iter in threadToProcs) { System.Console.Write(iter.Key + ": "); foreach (var proc in iter.Value) { System.Console.Write(proc + " "); } System.Console.WriteLine(); } } // [Optional] // Remove unreachable procs. // These procedures are neither reachable from Main, nor from a thread entry. They can obviously not be called in any execution. var reachableProcs = new HashSet <string>(); foreach (var vals in threadToProcs.Values) { foreach (var proc in vals) { reachableProcs.Add(proc); } } foreach (var val in threadToProcs.Keys) { reachableProcs.Add(val); } removeUnreachable(reachableProcs); //////////////// Finally, Actually split the procedures when needed ////////////////// // For every procedure, find out how many threads contain it. We need as many copies of // that procedure. // Note: We do not want to duplicate procedures without implementation. //impls = from impl in prog.TopLevelDeclarations where impl is bpl.Implementation select impl as bpl.Implementation; var implNames = from impl in impls(originalProgram) select impl.Name; Dictionary <string, int> procCopies = new Dictionary <string, int>(); var procs = from proc in originalProgram.TopLevelDeclarations where proc is bpl.Procedure select proc as bpl.Procedure; foreach (var proc in procs) { procCopies[proc.Name] = 0; } foreach (var iter in threadToProcs) { foreach (var proc in iter.Value) { if (implNames.Contains(proc)) { procCopies[proc]++; } } } // Now duplicate // The new procedures and implementations for each thread // thread name --> (old procedure name --> new procedure) var newProcsPerThread = new Dictionary <string, Dictionary <string, bpl.Procedure> >(); // thread name --> (old procedure name --> new implementation) var newImplsPerThread = new Dictionary <string, Dictionary <string, bpl.Implementation> >(); // old name --> proc, old name --> impl var oldNameToImpl = new Dictionary <string, bpl.Implementation>(); foreach (var impl in impls(originalProgram)) { oldNameToImpl[impl.Name] = impl; } var oldNameToProc = new Dictionary <string, bpl.Procedure>(); foreach (var proc in procs) { oldNameToProc[proc.Name] = proc; } foreach (var elem in threadToProcs) { string threadName = elem.Key; newProcsPerThread[threadName] = new Dictionary <string, bpl.Procedure>(); newImplsPerThread[threadName] = new Dictionary <string, bpl.Implementation>(); foreach (var procName in elem.Value) { if (procCopies[procName] > 1) { // We will duplicate this procedure. // Make a copy of the procedure and implementation var dup = new cba.Util.FixedDuplicator(); var impl = (bpl.Implementation)dup.VisitDeclaration(oldNameToImpl[procName]); var proc = (bpl.Procedure)dup.VisitDeclaration(oldNameToProc[procName]); impl.Proc = proc; // Rename the new instances using thread id. impl.Name = con.getSplitProcName(procName); proc.Name = con.getSplitProcName(procName); var origProcAttr = con.originalProcAttr(procName); origProcAttr.Next = proc.Attributes; proc.Attributes = origProcAttr; // Also add an attribute to the definition specifying the thread. var threadAttr = con.getThreadAttr(threadName); threadAttr.Next = proc.Attributes; proc.Attributes = threadAttr; //add to duplicated procedures/impls newProcsPerThread[threadName][procName] = proc; newImplsPerThread[threadName][procName] = impl; //Add to the program originalProgram.AddTopLevelDeclaration(proc); originalProgram.AddTopLevelDeclaration(impl); var s1 = new HashSet <string>(); var s2 = new HashSet <string>(); foreach (var im in originalProgram.TopLevelDeclarations.OfType <bpl.Implementation>()) { s1.Add(im.Name); } foreach (var im in impls(originalProgram)) { s2.Add(im.Name); } // We have split away the procedure calls for one thread. procCopies[procName]--; } else if (procCopies[procName] == 1) { //We still need to rename this procedure var impl = oldNameToImpl[procName]; var proc = oldNameToProc[procName]; //rename the new instances using thread id. impl.Name = con.getSplitProcName(procName); proc.Name = con.getSplitProcName(procName); var origProcAttr = con.originalProcAttr(procName); origProcAttr.Next = proc.Attributes; proc.Attributes = origProcAttr; // Also add an attribute to the definition specifying the thread. var threadAttr = con.getThreadAttr(threadName); threadAttr.Next = proc.Attributes; proc.Attributes = threadAttr; //add to duplicated procedures/impls newProcsPerThread[threadName][procName] = proc; newImplsPerThread[threadName][procName] = impl; // We have split away the procedure calls for one thread. procCopies[procName]--; } } // Tell con that we're done with one thread. con.nextThread(); } // New re-route procedure calls as needed // First async calls. foreach (var impl in impls(originalProgram)) { foreach (var blk in impl.Blocks) { for (int i = 0; i < blk.Cmds.Count; i++) { bpl.Cmd cmd = blk.Cmds[i]; if (con.isAsyncCall(cmd)) { var callCmd = cmd as bpl.CallCmd; var newCallCmd = new bpl.CallCmd(bpl.Token.NoToken, newProcsPerThread[callCmd.callee][callCmd.callee].Name, callCmd.Ins, callCmd.Outs, callCmd.Attributes, callCmd.IsAsync); newCallCmd.TypeParameters = callCmd.TypeParameters; newCallCmd.Proc = newProcsPerThread[callCmd.callee][callCmd.callee]; blk.Cmds[i] = newCallCmd; } } } } // Now sync calls. foreach (var elem in newImplsPerThread) { string threadName = elem.Key; var newImpls = newImplsPerThread[threadName]; foreach (var implTuple in newImpls) { var impl = implTuple.Value; foreach (var blk in impl.Blocks) { for (int i = 0; i < blk.Cmds.Count; ++i) { bpl.Cmd cmd = blk.Cmds[i]; if (con.isSyncCall(cmd) && newProcsPerThread[threadName].ContainsKey((cmd as bpl.CallCmd).callee)) { var callCmd = cmd as bpl.CallCmd; var newCallCmd = new bpl.CallCmd(bpl.Token.NoToken, newProcsPerThread[threadName][callCmd.callee].Name, callCmd.Ins, callCmd.Outs, callCmd.Attributes, callCmd.IsAsync); newCallCmd.TypeParameters = callCmd.TypeParameters; newCallCmd.Proc = newProcsPerThread[threadName][callCmd.callee]; blk.Cmds[i] = newCallCmd; } } } } } // Finally, label the entry of each thread as thread entry. foreach (var elem in newProcsPerThread) { var proc = elem.Value[elem.Key]; var threadEntryAttr = con.getThreadEntryAttr(); threadEntryAttr.Next = proc.Attributes; proc.Attributes = threadEntryAttr; } return(originalProgram); }