Exemplo n.º 1
0
        private static void ComputeCallGraph(Program program)
        {
            program.TopLevelDeclarations.OfType <Implementation>()
            .Iter(impl =>
            {
                Succ.Add(impl.Name, new HashSet <Implementation>());
                Pred.Add(impl.Name, new HashSet <Implementation>());
            });

            var name2Impl = BoogieUtil.nameImplMapping(program);

            foreach (var impl in program.TopLevelDeclarations.OfType <Implementation>())
            {
                foreach (var blk in impl.Blocks)
                {
                    foreach (var cmd in blk.Cmds.OfType <CallCmd>())
                    {
                        if (!Succ.ContainsKey(cmd.callee))
                        {
                            continue;
                        }
                        Succ[impl.Name].Add(name2Impl[cmd.callee]);
                        Pred[cmd.callee].Add(impl);
                    }
                }
            }
        }
        private static void initialize(Program program, ErrorTrace trace, string filename)
        {
            // Initialization
            fileName     = filename == null ? "null" : filename;
            nameImplMap  = BoogieUtil.nameImplMapping(program);
            events       = new List <Event>();
            threadStacks = new Dictionary <int, List <WorkItem> >();
            tidCounter   = 0;

            varNamesChanged = false;
            varNameMap      = new Dictionary <string, string>();
            var globals = BoogieUtil.GetGlobalVariables(program);

            globals.Iter(g => varNameMap.Add(g.Name + "__0", g.Name));
        }
Exemplo n.º 3
0
        public RestrictToTrace(Program p, InsertionTrans t)
        {
            inp = p;

            usedNames                 = new Dictionary <string, int>();
            nameToImpl                = BoogieUtil.nameImplMapping(inp);
            nameToProc                = BoogieUtil.nameProcMapping(inp);
            calledProcsNoImpl         = new HashSet <string>();
            output                    = new Program();
            tinfo                     = t;
            newFixedContextProcs      = new HashSet <int>();
            addRaiseExceptionProcDecl = false;
            concretizeConstantToCall  = new Dictionary <string, int>();
            uvalueToConstants         = new Dictionary <string, HashSet <Constant> >();
        }
Exemplo n.º 4
0
        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, VCExprNAry> > >();
            this.impl2Summary         = new Dictionary <string, ISummaryElement>();
            this.name2Impl            = BoogieUtil.nameImplMapping(program);

            this.vcgen    = new VCGen(program, CommandLineOptions.Clo.ProverLogFilePath, CommandLineOptions.Clo.ProverLogFileAppend, new List <Checker>());
            this.prover   = ProverInterface.CreateProver(program, CommandLineOptions.Clo.ProverLogFilePath, CommandLineOptions.Clo.ProverLogFileAppend, CommandLineOptions.Clo.TimeLimit);
            this.reporter = new AbstractHoudiniErrorReporter();

            var impls = new List <Implementation>(
                program.TopLevelDeclarations.OfType <Implementation>());

            // Create all VCs
            impls
            .Iter(attachEnsures);

            impls
            .Iter(GenVC);
        }
Exemplo n.º 5
0
        public override CBAProgram runCBAPass(CBAProgram p)
        {
            procsOnEP = new HashSet <string>();
            var nameImplMap = BoogieUtil.nameImplMapping(p);

            // Set of all procedures (with an implementation)
            var allProcs = new HashSet <string>();

            foreach (var tp in nameImplMap)
            {
                allProcs.Add(tp.Key);
            }

            var verifier = getVerifier();

            // Run verification, gather traces
            verifier.run(input);

            // Set verification result
            success = verifier.success;
            traces  = verifier.traces;

            // Now, compute the error projection
            if (verifier.success)
            {
                ErrorProjection = allProcs;
                return(null);
            }

            // Look at all procedures that lie on the error trace
            foreach (var trace in verifier.traces)
            {
                procsOnEP.UnionWith(trace.getProcs());
            }

            Log.WriteLine(Log.Normal, string.Format("EP: Got {0} traces and {1} procs", verifier.traces.Count, procsOnEP.Count));

            // Just make sure that we inlcude main here (probably not necessary)
            procsOnEP.Add(p.mainProcName);

            // Have we already covered all of the program?
            if (procsOnEP.Equals(allProcs))
            {
                ErrorProjection = new HashSet <string>();
                return(null);
            }

            // Iterate and try to force the verifier to return paths in
            // different procedures
            var done = false;

            do
            {
                var moreProcs = iterateComputation(input as PersistentCBAProgram, HashSetExtras <string> .Difference(allProcs, procsOnEP));
                if (moreProcs.Count == 0)
                {
                    done = true;
                }
                else
                {
                    procsOnEP.UnionWith(moreProcs);
                    if (procsOnEP.Equals(allProcs))
                    {
                        done = true;
                    }
                }
            } while (!done);

            ErrorProjection = HashSetExtras <string> .Difference(allProcs, procsOnEP);

            return(null);
        }
Exemplo n.º 6
0
        private PersistentCBAProgram instrument(PersistentCBAProgram pprogram, HashSet <string> candidates)
        {
            Debug.Assert(candidates.Count != 0);

            labelProcMap = new Dictionary <string, string>();
            var program     = pprogram.getProgram();
            var nameImplMap = BoogieUtil.nameImplMapping(program);

            // Each candidate gets its own variable
            var procVars = new Dictionary <string, Variable>();

            foreach (var str in candidates)
            {
                procVars.Add(str,
                             new GlobalVariable(Token.NoToken,
                                                new TypedIdent(Token.NoToken, "ep_var_" + str, Microsoft.Boogie.Type.Bool)));

                program.AddTopLevelDeclaration(procVars[str]);
            }

            // Add the new variables to each procedures' modifies clause
            foreach (var decl in program.TopLevelDeclarations)
            {
                if (decl is Implementation)
                {
                    var impl = decl as Implementation;
                    foreach (var str in candidates)
                    {
                        impl.Proc.Modifies.Add(new IdentifierExpr(Token.NoToken, procVars[str]));
                    }
                }
            }

            // Instrument program
            foreach (var str in candidates)
            {
                var impl  = nameImplMap[str];
                var ncmds = new List <Cmd>();
                ncmds.Add(BoogieAstFactory.MkVarEqConst(procVars[str], false));
                ncmds.AddRange(impl.Blocks[0].Cmds);

                impl.Blocks[0].Cmds = ncmds;
            }

            // Instrument main.
            // -- Assign true to the new variables
            // -- Change the assert
            //    (We assume that main only has one assert)

            var mainImpl = nameImplMap[pprogram.mainProcName];
            var newCmds  = new List <Cmd>();

            foreach (var str in candidates)
            {
                newCmds.Add(BoogieAstFactory.MkVarEqConst(procVars[str], true));
            }
            newCmds.AddRange(mainImpl.Blocks[0].Cmds);
            mainImpl.Blocks[0].Cmds = newCmds;

            // Find the assert
            Block     blkWithAssert = null;
            AssertCmd assertCmd     = null;
            bool      found         = false;

            foreach (var blk in mainImpl.Blocks)
            {
                found = false;
                foreach (var cmd in blk.Cmds)
                {
                    if (cmd is AssertCmd)
                    {
                        assertCmd = cmd as AssertCmd;
                        found     = true;
                        break;
                    }
                }

                if (found)
                {
                    blkWithAssert = blk;
                    break;
                }
            }

            Debug.Assert(blkWithAssert != null);

            // Construct the new blocks
            var newLabs = new Dictionary <string, string>();
            var restLab = getNewLabel();

            foreach (var str in candidates)
            {
                newLabs.Add(str, getNewLabel());
                var tcmds = new List <Cmd>();

                tcmds.Add(new AssertCmd(Token.NoToken, Expr.Or(Expr.Ident(procVars[str]), assertCmd.Expr)));

                mainImpl.Blocks.Add(new Block(Token.NoToken, newLabs[str], tcmds, BoogieAstFactory.MkGotoCmd(restLab)));
            }

            // change blkWithAssert to include only commands upto the assert
            // Add the rest of commands to a new block
            found   = false;
            newCmds = new List <Cmd>();
            var newBlk = new Block(Token.NoToken, restLab, new List <Cmd>(), blkWithAssert.TransferCmd);

            foreach (Cmd cmd in blkWithAssert.Cmds)
            {
                if (cmd is AssertCmd)
                {
                    found = true;
                    continue;
                }
                if (!found)
                {
                    newCmds.Add(cmd);
                }
                else
                {
                    newBlk.Cmds.Add(cmd);
                }
            }
            blkWithAssert.Cmds = newCmds;
            var targets = new List <String>(newLabs.Values.ToArray());

            blkWithAssert.TransferCmd = new GotoCmd(Token.NoToken, targets);

            mainImpl.Blocks.Add(newBlk);

            var ret = new PersistentCBAProgram(program, pprogram.mainProcName, pprogram.contextBound);

            //ret.writeToFile("ep_instrumented.bpl");

            return(ret);
        }
Exemplo n.º 7
0
        public override CBAProgram runCBAPass(CBAProgram program)
        {
            var nameImplMap = BoogieUtil.nameImplMapping(program);

            // Construct call graph, compute SCCs
            var graph = new Graph <string>();

            foreach (var impl in program.TopLevelDeclarations.OfType <Implementation>())
            {
                impl.Blocks.Iter(block =>
                                 block.Cmds.OfType <CallCmd>().Iter(cmd =>
                                                                    graph.AddEdge(impl.Name, cmd.callee)));
            }
            graph.AddSource(program.mainProcName);
            var preds = new Adjacency <string>(st => graph.Predecessors(st));
            var succs = new Adjacency <string>(st => graph.Successors(st));
            var sccs  = new StronglyConnectedComponents <string>(graph.Nodes, preds, succs);

            sccs.Compute();

            //var dotFileCnt = 1;

            // For each SCC, compute backedges
            foreach (var scc in sccs)
            {
                if (scc.Count == 1)
                {
                    var onlyProc = scc.First();
                    if (nameImplMap.ContainsKey(onlyProc) && QKeyValue.FindBoolAttribute(nameImplMap[onlyProc].Attributes, "LoopProcedure"))
                    {
                        continue;
                    }

                    if (graph.Successors(onlyProc).All(callee => callee != onlyProc))
                    {
                        continue;
                    }
                }

                Console.Write("Considering SCC: ");
                scc.Iter(s => Console.Write("{0} ", s));
                Console.WriteLine();

                foundRecursion = true;

                // pick source
                var sccProcs = new HashSet <string>(scc);
                var src      = scc.FirstOrDefault(proc => graph.Predecessors(proc).Any(pred => !sccProcs.Contains(pred)));
                if (src == null)
                {
                    src = scc.First();
                }

                var grey  = new HashSet <string>();
                var black = new HashSet <string>();
                grey.Add(src);

                backedges     = new HashSet <Tuple <string, string> >();
                dfsTreeParent = new Dictionary <string, string>();
                someCycles    = new List <HashSet <string> >();

                dfs(graph, src, sccProcs, grey, black);

                InferWhatToCut(graph, scc);

                // create copies
                var procCopies = new Dictionary <Tuple <string, int>, Procedure>();
                var implCopies = new Dictionary <Tuple <string, int>, Implementation>();
                foreach (var name in scc)
                {
                    var impl = nameImplMap[name];
                    program.RemoveTopLevelDeclaration(impl);
                    program.RemoveTopLevelDeclaration(impl.Proc);

                    for (int i = 0; i < CommandLineOptions.Clo.RecursionBound; i++)
                    {
                        var dup   = new FixedDuplicator(true);
                        var nimpl = dup.VisitImplementation(impl);
                        var nproc = dup.VisitProcedure(impl.Proc);

                        nimpl.Name += string.Format("#{0}", i);
                        nproc.Name += string.Format("#{0}", i);
                        nimpl.Proc  = nproc;

                        program.AddTopLevelDeclaration(nimpl);
                        program.AddTopLevelDeclaration(nproc);

                        procCopies.Add(Tuple.Create(impl.Name, i), nproc);
                        implCopies.Add(Tuple.Create(impl.Name, i), nimpl);
                    }
                }

                // redirect calls
                foreach (var name in scc)
                {
                    foreach (var pred in graph.Predecessors(name))
                    {
                        if (sccProcs.Contains(pred))
                        {
                            continue;
                        }
                        var pimpl = nameImplMap[pred];
                        foreach (var blk in pimpl.Blocks)
                        {
                            var newcmds = new List <Cmd>();
                            foreach (var cmd in blk.Cmds)
                            {
                                var ccmd = cmd as CallCmd;
                                if (ccmd == null || !sccProcs.Contains(ccmd.callee))
                                {
                                    newcmds.Add(cmd);
                                    continue;
                                }
                                newcmds.Add(
                                    new CallCmd(ccmd.tok, ccmd.callee + string.Format("#{0}", CommandLineOptions.Clo.RecursionBound - 1),
                                                ccmd.Ins, ccmd.Outs, ccmd.Attributes, ccmd.IsAsync));
                            }
                            blk.Cmds = newcmds;
                        }
                    }

                    for (int i = 0; i < CommandLineOptions.Clo.RecursionBound; i++)
                    {
                        var impl = implCopies[Tuple.Create(name, i)];
                        foreach (var blk in impl.Blocks)
                        {
                            var newcmds = new List <Cmd>();
                            foreach (var cmd in blk.Cmds)
                            {
                                var ccmd = cmd as CallCmd;
                                if (ccmd == null || !sccProcs.Contains(ccmd.callee))
                                {
                                    newcmds.Add(cmd);
                                    continue;
                                }
                                var cnt = i;
                                if (CutEdge(name, ccmd.callee))
                                {
                                    cnt--;
                                }

                                if (cnt < 0)
                                {
                                    newcmds.Add(new AssumeCmd(Token.NoToken, Expr.False));
                                }
                                else
                                {
                                    newcmds.Add(new CallCmd(ccmd.tok, ccmd.callee + string.Format("#{0}", cnt),
                                                            ccmd.Ins, ccmd.Outs, ccmd.Attributes, ccmd.IsAsync));
                                }
                            }
                            blk.Cmds = newcmds;
                        }
                    }
                }
            }

            return(program);
        }
        public override CBAProgram runCBAPass(CBAProgram program)
        {
            // Add blanks
            blanksInfo = AddBlanks(program);

            // Remove unreachable procedures
            BoogieUtil.pruneProcs(program, program.mainProcName);

            if (!Options.TraceSlicing)
            {
                // Remove source line annotations
                sourceInfo = cba.PrintSdvPath.DeleteSourceInfo(program);
            }
            else
            {
                sourceInfo = null;
            }

            // Remove print info
            //printInfo = cba.PrintSdvPath.DeletePrintCmds(program);

            // Compress
            compressBlocks.VisitProgram(program);

            // name Ebasic
            NameEnvironmentConstraints(program);

            // Instrument assertions
            int tokenId = 0;

            origMainName = program.mainProcName;

            CBAProgram ret = null;

            if (!Options.DeepAsserts)
            {
                // Do error-bit instrumentation
                var impls = BoogieUtil.nameImplMapping(program);
                var pwa   = cba.SequentialInstrumentation.procsWithAsserts(program);

                foreach (var impl in program.TopLevelDeclarations.OfType <Implementation>())
                {
                    var instrumented = new List <Block>();
                    foreach (var blk in impl.Blocks)
                    {
                        var currCmds  = new List <Cmd>();
                        var currLabel = blk.Label;

                        assertInstrInfo.addTrans(impl.Name, blk.Label, blk.Label);
                        var incnt = -1;
                        foreach (Cmd cmd in blk.Cmds)
                        {
                            incnt++;

                            // instrument assert
                            if (cmd is AssertCmd && !BoogieUtil.isAssertTrue(cmd))
                            {
                                currCmds.Add(BoogieAstFactory.MkVarEqExpr(assertsPassed, (cmd as AssertCmd).Expr));

                                var token = new AssertToken(tokenId);
                                originalAssertions.Add(token, cmd as AssertCmd);
                                tokenLocation.Add(token, Tuple.Create(impl.Name, currLabel));
                                procToTokens.InitAndAdd(impl.Name, token);

                                addedTrans(impl.Name, blk.Label, incnt, cmd, currLabel, currCmds);

                                currLabel = addInstr(instrumented, currCmds, currLabel, tokenId);
                                tokenId++;
                                currCmds = new List <Cmd>();

                                continue;
                            }

                            // procedure call
                            if (cmd is CallCmd && pwa.Contains((cmd as CallCmd).callee))
                            {
                                currCmds.Add(cmd);
                                addedTrans(impl.Name, blk.Label, incnt, cmd, currLabel, currCmds);
                                currLabel = addInstr(instrumented, currCmds, currLabel, -1);
                                currCmds  = new List <Cmd>();
                                continue;
                            }

                            currCmds.Add(cmd);
                            addedTrans(impl.Name, blk.Label, incnt, cmd, currLabel, currCmds);
                        }

                        instrumented.Add(new Block(Token.NoToken, currLabel, currCmds, blk.TransferCmd));
                    }

                    impl.Blocks = instrumented;
                }

                program.AddTopLevelDeclaration(assertsPassed);
                var newMain = addMain(program);

                BoogieUtil.DoModSetAnalysis(program);

                // Set inline attribute
                // free requires assertsPassed == true;
                foreach (var impl in program.TopLevelDeclarations.OfType <Implementation>())
                {
                    impl.Proc.Requires.Add(new Requires(true, Expr.Ident(assertsPassed)));
                }

                // convert free ensures e to:
                //  free ensures assertsPassed == false || e
                foreach (var impl in program.TopLevelDeclarations.OfType <Implementation>()
                         .Where(impl => pwa.Contains(impl.Name)))
                {
                    foreach (Ensures ens in impl.Proc.Ensures)
                    {
                        ens.Condition = Expr.Or(Expr.Not(Expr.Ident(assertsPassed)), ens.Condition);
                    }
                }

                currProg = program;
                ret      = new CBAProgram(program, newMain, 0);
            }
            else
            {
                // Use Deep-assert instrumentation
                da = new cba.DeepAssertRewrite();

                // First, tag assertions with tokens
                foreach (var impl in program.TopLevelDeclarations.OfType <Implementation>())
                {
                    foreach (var blk in impl.Blocks)
                    {
                        foreach (var cmd in blk.Cmds.OfType <AssertCmd>())
                        {
                            if (BoogieUtil.isAssertTrue(cmd))
                            {
                                continue;
                            }

                            var token = new AssertToken(tokenId);
                            cmd.Attributes = new QKeyValue(Token.NoToken, "avn", new List <object> {
                                Expr.Literal(tokenId)
                            },
                                                           cmd.Attributes);
                            originalAssertions.Add(token, cmd);
                            tokenLocation.Add(token, Tuple.Create(impl.Name, blk.Label));
                            procToTokens.InitAndAdd(impl.Name, token);
                            tokenId++;
                        }
                    }
                }

                // Second, do the rewrite
                var t1     = new PersistentProgram(program, program.mainProcName, program.contextBound);
                var t2     = da.run(t1);
                var daprog = t2.getCBAProgram();

                // Third, revisit the assertions and remember their location
                // in the output program. This is a bit of a hack. The "tokenLocation"
                // of a token is the pair (p1,b1) where p1 is the procedure the assertion
                // originally came from and b1 is the block in the new main that contains
                // that assertion.
                var main = BoogieUtil.findProcedureImpl(daprog.TopLevelDeclarations, daprog.mainProcName);
                foreach (var block in main.Blocks)
                {
                    foreach (var cmd in block.Cmds.OfType <AssertCmd>())
                    {
                        var tok = QKeyValue.FindIntAttribute(cmd.Attributes, "avn", -1);
                        if (tok < 0)
                        {
                            continue;
                        }
                        var token = new AssertToken(tok);

                        Debug.Assert(tokenLocation.ContainsKey(token));
                        var oldloc = tokenLocation[token];
                        tokenLocation[token] = Tuple.Create(oldloc.Item1, block.Label);
                    }
                }

                currProg = daprog;
                ret      = daprog;
            }

            return(ret);
        }
Exemplo n.º 9
0
        public override CBAProgram runCBAPass(CBAProgram p)
        {
            // Run verification, gather traces
            var verifier = getVerifier();

            verifier.run(input);

            // Set verification result
            success = verifier.success;
            traces  = verifier.traces;

            // Now compute coverage, provided error was not reached
            if (!verifier.success)
            {
                return(null);
            }

            // Gather procedure names
            var nameImplMap = BoogieUtil.nameImplMapping(p);

            var allProcs = new HashSet <string>();

            foreach (var tp in nameImplMap)
            {
                allProcs.Add(tp.Key);
            }

            procsNotCovered = new HashSet <string>();
            procsNotCovered.UnionWith(allProcs);
            procsNotCovered.Remove(p.mainProcName);

            // Iterate and gather procedures that can be reached
            int oldProverLimit = CommandLineOptions.Clo.ProverCCLimit;

            var done = false;

            do
            {
                // Set the number of traces returned by boogie in one shot
                CommandLineOptions.Clo.ProverCCLimit = procsNotCovered.Count();

                var covered = iterateComputation(input as PersistentCBAProgram, procsNotCovered);
                if (covered.Count == 0)
                {
                    done = true;
                }
                else
                {
                    procsNotCovered = HashSetExtras <string> .Difference(procsNotCovered, covered);

                    if (!procsNotCovered.Any())
                    {
                        done = true;
                    }
                }
            } while (!done);

            CommandLineOptions.Clo.ProverCCLimit = oldProverLimit;

            return(null);
        }
Exemplo n.º 10
0
        // 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());
                }
            }
        }
Exemplo n.º 11
0
        private ErrorTrace mapBackTraceInline(ErrorTrace trace)
        {
            Debug.Assert(trace.isIntra());

            Program inputProg = inlineInput.getProgram();

            var nameImplMap = BoogieUtil.nameImplMapping(inputProg);
            var traceLabels = trace.getBlockLabels();

            var            stack     = new List <WorkItemR>();
            Implementation startImpl = getImpl(trace.procName, nameImplMap);

            Debug.Assert(traceLabels[0] == startImpl.Blocks[0].Label);
            var curr = new WorkItemR(startImpl, traceLabels[0]);

            // This variable keeps our current position in traceLabels
            int ecount = 0;
            // This variable keeps our current position in traceLabels[ecount].Cmds
            int icount = 0;

            var ret = new ErrorTrace(trace.procName);

            ret.addBlock(new ErrorTraceBlock(traceLabels[0]));

            // We will walk over the input program, trying to cover the same path as
            // trace, knowing that trace represents an inlined path
            while (true)
            {
                // Reached the end of the path?
                if (ecount == traceLabels.Count)
                {
                    break;
                }

                //Console.WriteLine(curr.impl.Name + ": " + curr.label);

                // Reached the end of the current block?
                if (curr.count == curr.block.Cmds.Count)
                {
                    ecount++;
                    icount = 0;
                    if (ecount == traceLabels.Count)
                    {
                        break;
                    }

                    // Move onto the next block
                    TransferCmd tc = curr.block.TransferCmd;
                    if (tc is ReturnCmd)
                    {
                        ret.addReturn();

                        if (stack.Count == 0)
                        {
                            // We're done
                            Debug.Assert(ecount == traceLabels.Count);
                            break;
                        }
                        curr = stack[0];
                        stack.RemoveAt(0);

                        // An inlined procedure ends with "Return" label
                        Debug.Assert(inlinedLabelMatches("Return", traceLabels[ecount]));

                        ecount++;
                        icount = 0;
                        Debug.Assert(traceLabels[ecount].Contains(curr.block.Label));

                        continue;
                    }

                    if (tc is GotoCmd)
                    {
                        List <String> targets = (tc as GotoCmd).labelNames;
                        string        target  = matchInlinedLabelNames(targets, traceLabels[ecount]);
                        curr = new WorkItemR(curr.impl, target);
                        ret.addBlock(new ErrorTraceBlock(curr.label));
                        continue;
                    }

                    // Unknown transfer command
                    Debug.Assert(false);
                }

                // We have to continue in the same block
                Cmd c = curr.block.Cmds[curr.count];
                curr.count++;

                if (!(c is CallCmd))
                {
                    ret.addInstr(new IntraInstr(getInfo(trace, ecount, icount)));
                    icount++;
                    continue;
                }

                // We're at a procedure call
                CallCmd cc = c as CallCmd;

                // If this is a call to a procedure without implementation, then skip
                if (!nameImplMap.ContainsKey(cc.Proc.Name))
                {
                    ret.addInstr(new CallInstr(cc.Proc.Name, null, false, getInfo(trace, ecount, icount)));
                    icount++;
                    continue;
                }

                Implementation callee = getImpl(cc.Proc.Name, nameImplMap);
                string         label  = callee.Blocks[0].Label;
                // The first label in a inlined procedure is always called Entry
                ecount++;
                Debug.Assert(inlinedLabelMatches("Entry", traceLabels[ecount]));

                ecount++;
                icount = 0;
                Debug.Assert(inlinedLabelMatches(label, traceLabels[ecount]));

                WorkItemR next = new WorkItemR(callee, label);
                stack.Insert(0, curr);
                curr = next;

                ret.addCall(callee.Name);
                ret.addBlock(new ErrorTraceBlock(curr.label));
            }
            return(ret);
        }