public WorkItemR(Implementation i, string l) { impl = i; labelBlockMap = BoogieUtil.labelBlockMapping(impl); label = l; block = getBlock(label, labelBlockMap); count = 0; }
// Rename basic blocks, local variables // Add "havoc locals" at the beginning // block return private static void RenameImpl(Implementation impl, Dictionary <string, Tuple <Block, Implementation> > origProg) { var origImpl = (new FixedDuplicator(true)).VisitImplementation(impl); var origBlocks = BoogieUtil.labelBlockMapping(origImpl); // create new locals var newLocals = new Dictionary <string, Variable>(); foreach (var l in impl.LocVars.Concat(impl.InParams).Concat(impl.OutParams)) { // substitute even formal variables with LocalVariables. This is fine // because we finally just merge all implemnetations together var nl = BoogieAstFactory.MkLocal(l.Name + "_" + impl.Name + "_copy", l.TypedIdent.Type); newLocals.Add(l.Name, nl); } // rename locals var subst = new VarSubstituter(newLocals, new Dictionary <string, Variable>()); subst.VisitImplementation(impl); // Rename blocks foreach (var blk in impl.Blocks) { var newName = impl.Name + "_" + blk.Label; origProg.Add(newName, Tuple.Create(origBlocks[blk.Label], origImpl)); blk.Label = newName; if (blk.TransferCmd is GotoCmd) { var gc = blk.TransferCmd as GotoCmd; gc.labelNames = new List <string>( gc.labelNames.Select(lab => impl.Name + "_" + lab)); } if (blk.TransferCmd is ReturnCmd) { // block return blk.Cmds.Add(new AssumeCmd(Token.NoToken, Expr.False)); } } /* * // havoc locals -- not necessary * if (newLocals.Count > 0) * { * var ies = new List<IdentifierExpr>(); * newLocals.Values.Iter(v => ies.Add(Expr.Ident(v))); * impl.Blocks[0].Cmds.Insert(0, new HavocCmd(Token.NoToken, ies)); * } */ }
// Note: this does not reconstruct the failing assert in trace public static void ReconstructImperativeTrace(Counterexample trace, string currProc, Dictionary <string, Implementation> origProg) { if (trace == null) { return; } var originalBlocks = BoogieUtil.labelBlockMapping(origProg[currProc]); var newBlocks = new List <Block>(); var newCalleeTraces = new Dictionary <TraceLocation, CalleeCounterexampleInfo>(); for (int numBlock = 0; numBlock < trace.Trace.Count; numBlock++) { Block b = trace.Trace[numBlock]; Block ib; originalBlocks.TryGetValue(b.Label, out ib); if (ib == null) { // Such blocks correspond to "itermediate" blocks inserted // by Boogie. We can ignore them. (But note that we should // still check that the counterexample is a valid path in impl // to guard against vagaries of Boogie.) //Log.Out(Log.Normal, "Could not find block " + b.Label); //b.Emit(new TokenTextWriter(Console.Out), 0); for (int numInstr = 0; numInstr < b.Cmds.Count; numInstr++) { if (trace.calleeCounterexamples.ContainsKey(new TraceLocation(numBlock, numInstr))) { throw new InternalError("BoogieVerify: An intermediate block has a procedure call"); } } } else { // We have a corresponding block. The number of Commands in b and ib won't match. We // simply use all of the Cmds of ib -- and match the calls manually // TODO: Fix this! It doesn't work when the failing assert is not the last statement // of the block newBlocks.Add(ib); var calleeTraces = new List <Duple <string, CalleeCounterexampleInfo> >(); for (int numInstr = 0; numInstr < b.Cmds.Count; numInstr++) { var loc = new TraceLocation(numBlock, numInstr); if (trace.calleeCounterexamples.ContainsKey(loc)) { Cmd c = b.Cmds[numInstr]; var calleeName = trace.getCalledProcName(c); var calleeTrace = trace.calleeCounterexamples[loc].counterexample; ReconstructImperativeTrace(calleeTrace, calleeName, origProg); calleeTraces.Add( new Duple <string, CalleeCounterexampleInfo>( calleeName, new CalleeCounterexampleInfo(calleeTrace, trace.calleeCounterexamples[loc].args) )); } } // Check consistency and map calleeTraces to the actual call instructions var currCount = 0; for (int numInstr = 0; numInstr < ib.Cmds.Count; numInstr++) { Cmd c = ib.Cmds[numInstr]; if (!(c is CallCmd)) { continue; } var cc = c as CallCmd; // No more calls left to process if (calleeTraces.Count <= currCount) { break; } if (cc.Proc.Name != calleeTraces[currCount].fst) { continue; } // Check if this proc has an implementation //if (!origProg.ContainsKey(cc.Proc.Name)) // continue; // Check if the proc is inlined //if (QKeyValue.FindExprAttribute(cc.Proc.Attributes, "inline") == null) // continue; // Some thing wrong about the interprocedural trace returned by Boogie if the // following don't match // TODO: Fix when the failing assert is not the last statement of the block //Debug.Assert(cc.Proc.Name == calleeTraces[currCount].fst); newCalleeTraces.Add(new TraceLocation(newBlocks.Count - 1, numInstr), calleeTraces[currCount].snd); currCount++; } } } trace.Trace = newBlocks; // reset other info. Safe thing to do unless we know what it is trace.calleeCounterexamples = newCalleeTraces; }
// Prints trace by recursively calling itself on calleeTraces. This assumes // that info in each of the ErrorTraceInstr is valid. // Returns the value of k at the end of the trace private static int collectAllEvents(ErrorTrace trace) { Debug.Assert(trace.Blocks.Count != 0); int k = 0, tid = 1; Implementation impl = nameImplMap[trace.procName]; var nameBlockMap = BoogieUtil.labelBlockMapping(impl); lastCLocation = null; var first_block = true; // Walk through trace and impl in lock step foreach (var tblk in trace.Blocks) { Block pblk = nameBlockMap[tblk.blockName]; fetchInfo(tblk.info, out k, out tid); if (first_block) { pushWI(tid, new WorkItem(trace.procName, null, null)); } first_block = false; updateWI(tid, pblk.tok as Token); updateWI(tid, pblk.Label); updateCLocation(tid); var assertFails = ""; if (tblk.info is AssertFailInstrInfo) { var extra = (tblk.info is RequiresFailInstrInfo) ? ": Requires" : (tblk.info is EnsuresFailInstrInfo) ? ": Ensures" : ""; assertFails = "ASSERTION FAILS" + extra; } events.Add(new Event(k, tid, printStack(tid), getFileName(tid), getLineNo(tid), getColNo(tid), assertFails, true)); int pcnt = 0; int tcnt = 0; foreach (var tcmd in tblk.Cmds) { fetchInfo(tcmd.info, out k, out tid); updateWI(tid, pblk.Cmds[pcnt].tok as Token); assertFails = ""; if (tcmd.info is AssertFailInstrInfo) { var extra = (tcmd.info is RequiresFailInstrInfo) ? ": Requires" : (tcmd.info is EnsuresFailInstrInfo) ? ": Ensures" : ""; assertFails = "ASSERTION FAILS " + (pblk.Cmds[pcnt].ToString()) + " " + extra; } if (tcmd.info is ModelInstrInfo) { assertFails = tcmd.info.ToString(); changeVarNames((tcmd.info as ModelInstrInfo).model); } if (tcmd.info is PrintInstrInfo) { assertFails += tcmd.info.ToString(); } if (tcmd.isCall()) { Debug.Assert(pblk.Cmds[pcnt] is CallCmd); CallInstr cc = tcmd as CallInstr; string callstr = string.Format("{0} {1}", cc.asyncCall ? "FORK" : "CALL", (pblk.Cmds[pcnt] as CallCmd).Proc.Name); if (!cc.hasCalledTrace) { callstr = ""; if (cc.callee.StartsWith(VerificationPass.recordArgProcPrefix)) { var cmd = pblk.Cmds[pcnt] as CallCmd; Debug.Assert(cmd.Ins.Count == 1); var prefix = QKeyValue.FindStringAttribute(cmd.Attributes, "cexpr"); if (prefix == null) { prefix = "v"; } prefix += " = "; if (cc.info.hasVar("si_arg")) { callstr += prefix + cc.info.getVal("si_arg").ToString(); } } } var leftOverForReturn = ""; if (assertFails != "" && callstr != "") { // This can only be because of a failed requires/ensures leftOverForReturn = assertFails; } else { callstr = callstr + assertFails; } events.Add(new Event(k, tid, printStack(tid), getFileName(tid), getLineNo(tid), getColNo(tid), callstr, true)); if (cc.hasCalledTrace) { int oldk = k; k = collectAllEvents(cc.calleeTrace); if (cc.asyncCall) { k = oldk; } if (cc.calleeTrace.returns) { var extra = ""; if (!cc.asyncCall) { extra = "RETURN from " + (pblk.Cmds[pcnt] as CallCmd).Proc.Name; extra += " " + leftOverForReturn; } events.Add(new Event(k, tid, printStack(tid), getFileName(tid), getLineNo(tid), getColNo(tid), extra, true)); } } } else { events.Add(new Event(k, tid, printStack(tid), getFileName(tid), getLineNo(tid), getColNo(tid), "" + assertFails, true)); } pcnt++; tcnt++; } } if (first_block == false) { popWI(tid); } return(k); }
// Propagate levels static void doHwswLevelMatch(Implementation impl) { var labelBlockMap = BoogieUtil.labelBlockMapping(impl); foreach (var blk in impl.Blocks) { for (int i = 0; i < blk.Cmds.Count; i++) { var ccmd = blk.Cmds[i] as CallCmd; if (ccmd == null || (ccmd.callee != levelCall && ccmd.callee != cpuCall)) { continue; } if (ccmd.callee == levelCall) { // Get level if (ccmd.Ins.Count != 1) { throw new InternalError("Incorrect hwsw level usage"); } if (!(ccmd.Ins[0] is LiteralExpr)) { throw new InternalError("Incorrect hwsw level usage"); } var level = ((Microsoft.BaseTypes.BigNum)(ccmd.Ins[0] as LiteralExpr).Val).ToInt; // start search var currBlk = blk; var currCmd = i + 1; var done = false; while (!done) { for (int j = currCmd; j < currBlk.Cmds.Count && !done; j++) { var tc = currBlk.Cmds[j] as CallCmd; if (tc == null || !tc.IsAsync) { continue; } done = true; // change attribute var parameters = new List <object>(); parameters.Add(Expr.Literal(level)); tc.Attributes = new QKeyValue(Token.NoToken, "level", parameters, tc.Attributes); } if (!done) { var gc = currBlk.TransferCmd as GotoCmd; if (gc == null || gc.labelNames.Count != 1) { throw new InternalError("Incorrect hwsw level usage"); } currBlk = labelBlockMap[gc.labelNames[0]]; currCmd = 0; } } } else if (ccmd.callee == cpuCall) { // Get level if (ccmd.Ins.Count != 1) { throw new InternalError("Incorrect hwsw cpu usage"); } if (!(ccmd.Ins[0] is LiteralExpr)) { throw new InternalError("Incorrect hwsw cpu usage"); } var level = ((Microsoft.BaseTypes.BigNum)(ccmd.Ins[0] as LiteralExpr).Val).ToInt; // start search var currBlk = blk; var currCmd = i + 1; var done = false; while (!done) { for (int j = currCmd; j < currBlk.Cmds.Count && !done; j++) { var tc = currBlk.Cmds[j] as CallCmd; if (tc == null || !tc.IsAsync) { continue; } done = true; // change attribute var parameters = new List <object>(); parameters.Add(Expr.Literal(level)); tc.Attributes = new QKeyValue(Token.NoToken, "cpu", parameters, tc.Attributes); } if (!done) { var gc = currBlk.TransferCmd as GotoCmd; if (gc == null || gc.labelNames.Count != 1) { throw new InternalError("Incorrect hwsw cpu usage"); } currBlk = labelBlockMap[gc.labelNames[0]]; currCmd = 0; } } } } } }
private string addTraceRec(ErrorTrace trace) { Debug.Assert(trace.Blocks.Count != 0); // First, construct the procedure declaration: only a name change required string newName = getNewName(trace.procName); Procedure proc = nameToProc[trace.procName]; output.AddTopLevelDeclaration( new Procedure(Token.NoToken, newName, proc.TypeParameters, proc.InParams, proc.OutParams, proc.Requires, proc.Modifies, proc.Ensures, proc.Attributes)); // Now to peice together the commands from the implementation. We keep around // multiple blocks to make sure that trace mapping works out. var traceBlocks = new List <Block>(); Implementation impl = nameToImpl[trace.procName]; var labelToBlock = BoogieUtil.labelBlockMapping(impl); // These two need not agree. This happens when impl.Block[0] has // no commands. // Debug.Assert(impl.Blocks[0].Label == trace.Blocks[0].blockName); Debug.Assert(reachableViaEmptyBlocks(impl.Blocks[0].Label, labelToBlock).Contains(trace.Blocks[0].blockName)); for (int i = 0, n = trace.Blocks.Count; i < n; i++) { Block curr = labelToBlock[trace.Blocks[i].blockName]; Block traceBlock = new Block(); traceBlock.Cmds = new List <Cmd>(); traceBlock.Label = addIntToString(trace.Blocks[i].blockName, i); // (The "i" is to deal with loops) if (i != n - 1) { traceBlock.TransferCmd = BoogieAstFactory.MkGotoCmd(addIntToString(trace.Blocks[i + 1].blockName, i + 1)); } else { traceBlock.TransferCmd = new ReturnCmd(Token.NoToken); } tinfo.addTrans(newName, trace.Blocks[i].blockName, traceBlock.Label); #region Check consistency Debug.Assert(curr.Cmds.Count >= trace.Blocks[i].Cmds.Count); if (trace.Blocks[i].Cmds.Count != curr.Cmds.Count) { Debug.Assert((i == n - 1)); } if (curr.TransferCmd is ReturnCmd) { Debug.Assert(i == n - 1); } else if (curr.TransferCmd is GotoCmd) { List <String> targets = (curr.TransferCmd as GotoCmd).labelNames; // one of these targets should be the next label if (i != n - 1) { Debug.Assert(reachableViaEmptyBlocks(targets, labelToBlock).Contains(trace.Blocks[i + 1].blockName)); } } else { throw new InternalError("Unknown transfer command"); } #endregion // Index into trace.Blocks[i].Cmds int instrCount = -1; foreach (Cmd c in curr.Cmds) { instrCount++; if (instrCount == trace.Blocks[i].Cmds.Count) { break; } ErrorTraceInstr curr_instr = trace.Blocks[i].Cmds[instrCount]; if (curr_instr.info.isValid) { traceBlock.Cmds.Add(InstrumentationConfig.getFixedContextProc(curr_instr.info.executionContext)); newFixedContextProcs.Add(curr_instr.info.executionContext); } // Don't keep "fix context value" procs from the input program if (InstrumentationConfig.getFixedContextValue(c) != -1) { traceBlock.Cmds.Add(new AssumeCmd(Token.NoToken, Expr.True)); addedTrans(newName, curr.Label, instrCount, c, traceBlock.Label, traceBlock.Cmds); continue; } // Don't keep "fix raise exception" procs from the input program if (InstrumentationConfig.isFixedRaiseExceptionProc(c)) { traceBlock.Cmds.Add(new AssumeCmd(Token.NoToken, Expr.True)); addedTrans(newName, curr.Label, instrCount, c, traceBlock.Label, traceBlock.Cmds); continue; } if (addConcretization && c is HavocCmd && curr_instr.info != null && curr_instr.info.hasIntVar("si_arg") && (c as HavocCmd).Vars.Count > 0 && (c as HavocCmd).Vars[0].Decl.TypedIdent.Type.IsInt) { // concretize havoc-ed variable var val = curr_instr.info.getIntVal("si_arg"); traceBlock.Cmds.Add(BoogieAstFactory.MkVarEqConst((c as HavocCmd).Vars[0].Decl, val)); addedTrans(newName, curr.Label, instrCount, c, traceBlock.Label, traceBlock.Cmds); continue; } if (!(c is CallCmd)) { if (convertNonFailingAssertsToAssumes && c is AssertCmd && !(curr_instr.info is AssertFailInstrInfo)) { traceBlock.Cmds.Add(new AssumeCmd(c.tok, (c as AssertCmd).Expr, (c as AssertCmd).Attributes)); } else { traceBlock.Cmds.Add(c); } Debug.Assert(!curr_instr.isCall()); addedTrans(newName, curr.Label, instrCount, c, traceBlock.Label, traceBlock.Cmds); continue; } Debug.Assert(curr_instr.isCall()); CallCmd cc = c as CallCmd; CallInstr call_instr = curr_instr as CallInstr; if (!nameToImpl.ContainsKey(cc.Proc.Name) || !call_instr.hasCalledTrace) { // This is a call to a procedure without implementation; skip; calledProcsNoImpl.Add(cc.Proc.Name); traceBlock.Cmds.Add(c); Debug.Assert(!call_instr.hasCalledTrace); if (addConcretization && cc.Outs.Count == 1 && call_instr.info.hasVar("si_arg")) { if (!addConcretizationAsConstants) { if (call_instr.info.hasBoolVar("si_arg")) { traceBlock.Cmds.Add(BoogieAstFactory.MkVarEqConst(cc.Outs[0].Decl, call_instr.info.getBoolVal("si_arg"))); } else if (call_instr.info.hasIntVar("si_arg")) { traceBlock.Cmds.Add(BoogieAstFactory.MkVarEqConst(cc.Outs[0].Decl, call_instr.info.getIntVal("si_arg"))); } else { Debug.Assert(false); } } else { // create a constant that is equal to this literal, then use the constant // for concretization // do we use a specific name for the constant? var constantName = QKeyValue.FindStringAttribute(cc.Attributes, ConcretizeConstantNameAttr); if (constantName == null) { constantName = ""; } var constant = new Constant(Token.NoToken, new TypedIdent(Token.NoToken, string.Format("alloc_{0}__{1}", constantName, const_counter), cc.Outs[0].Decl.TypedIdent.Type), false); const_counter++; traceBlock.Cmds.Add(BoogieAstFactory.MkVarEqVar(cc.Outs[0].Decl, constant)); output.AddTopLevelDeclaration(constant); if (call_instr.info.hasIntVar("si_arg")) { output.AddTopLevelDeclaration(new Axiom(Token.NoToken, Expr.Eq(Expr.Ident(constant), Expr.Literal(call_instr.info.getIntVal("si_arg"))))); } else if (call_instr.info.hasVar("si_arg") && cc.Outs[0].Decl.TypedIdent.Type.IsCtor) { uvalueToConstants.InitAndAdd(call_instr.info.getVal("si_arg").ToString(), constant); } var id = QKeyValue.FindIntAttribute(cc.Attributes, ConcretizeCallIdAttr, -1); concretizeConstantToCall.Add(constant.Name, id); } } addedTrans(newName, curr.Label, instrCount, c, traceBlock.Label, traceBlock.Cmds); continue; } Debug.Assert(call_instr.calleeTrace.procName == cc.Proc.Name); var callee = addTraceRec(call_instr.calleeTrace); var newcmd = new CallCmd(Token.NoToken, callee, cc.Ins, cc.Outs, cc.Attributes, cc.IsAsync); traceBlock.Cmds.Add(newcmd); addedTrans(newName, curr.Label, instrCount, c, traceBlock.Label, traceBlock.Cmds); } traceBlocks.Add(traceBlock); } Debug.Assert(traceBlocks.Count != 0); if (trace.raisesException) { var exitblk = traceBlocks.Last(); exitblk.Cmds.Add(InstrumentationConfig.getFixedRaiseExceptionProc()); addRaiseExceptionProcDecl = true; } // Add the implementation to output //var block = new Block(Token.NoToken, trace.Blocks[0].blockName, traceCmdSeq, new ReturnCmd(Token.NoToken)); //List<Block> blocks = new List<Block>(); //blocks.Add(block); //tinfo.addTrans(newName, trace.Blocks[0].blockName, trace.Blocks[0].blockName); tinfo.addProcNameTrans(trace.procName, newName); output.AddTopLevelDeclaration( new Implementation(Token.NoToken, newName, impl.TypeParameters, impl.InParams, impl.OutParams, impl.LocVars, traceBlocks, QKeyValue.FindStringAttribute(impl.Attributes, "origRTname") == null ? new QKeyValue(Token.NoToken, "origRTname", new List <object> { impl.Name }, impl.Attributes) : impl.Attributes)); return(newName); }
// Short-circuit the chain of returns produced by raiseException public void optimizeRaiseExceptionInstrumentation(CBAProgram program) { var procMap = BoogieUtil.nameImplMapping(program); var mainImpl = procMap[program.mainProcName]; var labelBlockMap = BoogieUtil.labelBlockMapping(mainImpl); // For each block, check if the first statement is "assume raiseException" foreach (var blk in mainImpl.Blocks) { if (blk.Cmds.Count == 0) { continue; } var cmd = blk.Cmds[0]; if (!(cmd is AssumeCmd)) { continue; } var expr = (cmd as AssumeCmd).Expr; if (expr is IdentifierExpr) { var v = (expr as IdentifierExpr).Decl; if (v.Name != "raiseException") { continue; } } else { continue; } // Yup, its "assume raiseException" -- follow the chain of gotos through empty blocks var lab = getSingleSucc(blk.TransferCmd); if (lab == null) { continue; } var b = labelBlockMap[lab]; var chainLen = 0; /* Heuristic, possibly unsound: some blocks have a single statement that contains * an assignment for the return value. Note that once raiseException has been raised, * no return value is needed. */ while (b.Cmds.Count <= 1) { var next = getSingleSucc(b.TransferCmd); if (next == null) { break; } lab = next; b = labelBlockMap[lab]; chainLen++; } blk.TransferCmd = BoogieAstFactory.MkGotoCmd(lab); if (chainLen != 0) { Log.WriteLine(Log.Debug, "raiseException chain shortened by " + chainLen.ToString()); } } }