// goto label1, label2;
        //
        // label1:
        //   assume {:avn tok} !assertsPassed;
        //   return;
        //
        // label2:
        //   assume assertsPassed;
        //   goto lab;
        //
        // lab:
        //
        // Inputs: the list of blocks being constructed; the current block being constructed.
        // End current block and adds two new blocks.
        // Returns "lab".

        private string addInstr(List <Block> instrumented, List <Cmd> curr, string curr_label, int token)
        {
            string lbl1 = getNewLabel();
            string lbl2 = getNewLabel();

            List <String> ssp = new List <String> {
                lbl1, lbl2
            };

            instrumented.Add(new Block(Token.NoToken, curr_label, curr, new GotoCmd(Token.NoToken, ssp)));

            string common_label = getNewLabel();
            // assume (!assertsPassed)
            AssumeCmd cmd1 = new AssumeCmd(Token.NoToken, Expr.Not(Expr.Ident(assertsPassed)));

            if (token >= 0)
            {
                cmd1.Attributes = new QKeyValue(Token.NoToken, "avn", new List <object> {
                    Expr.Literal(token)
                }, cmd1.Attributes);
            }

            // assume (assertsPassed)
            AssumeCmd cmd2 = new AssumeCmd(Token.NoToken, Expr.Ident(assertsPassed));

            curr = new List <Cmd>();
            curr.Add(cmd1);
            instrumented.Add(new Block(Token.NoToken, lbl1, curr, new ReturnCmd(Token.NoToken)));

            curr = new List <Cmd>();
            curr.Add(cmd2);
            instrumented.Add(new Block(Token.NoToken, lbl2, curr, BoogieAstFactory.MkGotoCmd(common_label)));

            return(common_label);
        }
Exemplo n.º 2
0
        void Desugar(Variable lhs, Expr cond, Expr then, Expr els, ref List <Cmd> currCmds, ref Block currBlock, List <Block> newBlocks)
        {
            // create three new blocks
            var lab1 = GetNewLabel();
            var lab2 = GetNewLabel();
            var lab3 = GetNewLabel();

            var tmp = CreateTmp(btype.Bool);

            currCmds.Add(BoogieAstFactory.MkVarEqExpr(tmp, cond));

            // end current block
            currBlock.Cmds        = currCmds;
            currBlock.TransferCmd = new GotoCmd(Token.NoToken, new List <string> {
                lab1, lab2
            });
            newBlocks.Add(currBlock);


            var blk1 = new Block(Token.NoToken, lab1, new List <Cmd>(), BoogieAstFactory.MkGotoCmd(lab3));
            var blk2 = new Block(Token.NoToken, lab2, new List <Cmd>(), BoogieAstFactory.MkGotoCmd(lab3));

            blk1.Cmds.AddRange(
                new List <Cmd> {
                BoogieAstFactory.MkAssume(Expr.Ident(tmp)),
                BoogieAstFactory.MkVarEqExpr(lhs, then)
            });

            blk2.Cmds.AddRange(
                new List <Cmd> {
                BoogieAstFactory.MkAssume(Expr.Not(Expr.Ident(tmp))),
                BoogieAstFactory.MkVarEqExpr(lhs, els)
            });

            newBlocks.Add(blk1);
            newBlocks.Add(blk2);

            currBlock = new Block(Token.NoToken, lab3, new List <Cmd>(), null);
            currCmds  = new List <Cmd>();
        }
Exemplo n.º 3
0
        private static void InjectCode(Implementation impl, int anyParamsPosition, QKeyValue anyParamsAttributes, int anyParamsPositionOut, QKeyValue anyParamsAttributesOut,
                                       Implementation procSig, InsertAtBeginningRule rule, Dictionary <Declaration, Expr> paramSubstitution)
        {
            //TODO handle anyParams in the OutParam case
            var doesAnyParamOccurInRhs = false;

            if (anyParamsPosition != int.MaxValue)
            {
                var anyParam = procSig.InParams[anyParamsPosition];
                var oiv      = new OccursInVisitor(anyParam);
                oiv.VisitCmdSeq(rule.ProcedureToMatchToInsertion[procSig]);
                doesAnyParamOccurInRhs = oiv.Success;
            }

            if (doesAnyParamOccurInRhs)
            {
                for (int i = anyParamsPosition; i < impl.InParams.Count; i++)
                {
                    var p = impl.InParams[i];
                    // If attributes for the ##anyparams in the toMatch are given, we only insert code for those parameters of impl
                    // with matching (subset) attributes
                    // we look both in the implementation's and the procedure declaration's signature
                    if (anyParamsAttributes == null ||
                        ExprMatchVisitor.AreAttributesASubset(anyParamsAttributes, impl.Proc.InParams[i].Attributes))
                    {
                        if (!procSig.InParams[anyParamsPosition].TypedIdent.Type.Equals(p.TypedIdent.Type))
                        {
                            continue; //skip parameters that don't match type
                        }
                        var id           = new IdentifierExpr(Token.NoToken, p.Name, p.TypedIdent.Type, true);
                        var substitution = new Dictionary <Declaration, Expr> {
                            { procSig.InParams[anyParamsPosition], id }
                        };
                        foreach (var kvp in paramSubstitution)
                        {
                            substitution.Add(kvp.Key, kvp.Value);
                        }

                        var sv      = new SubstitionVisitor(substitution);
                        var newCmds = sv.VisitCmdSeq(rule.ProcedureToMatchToInsertion[procSig]);
                        if (impl.Blocks.Count > 0 && !QKeyValue.FindBoolAttribute(procSig.Attributes, ExprMatchVisitor.BoogieKeyWords.ReplaceImplementation))
                        {
                            impl.Blocks.Insert(0,
                                               BoogieAstFactory.MkBlock(newCmds,
                                                                        BoogieAstFactory.MkGotoCmd(impl.Blocks.First().Label)));
                        }
                        else
                        {
                            impl.Blocks = new List <Block>();
                            impl.Blocks.Add(
                                BoogieAstFactory.MkBlock(newCmds));
                        }
                    }
                }
            }
            else
            {
                var sv      = new SubstitionVisitor(paramSubstitution);
                var newCmds = sv.VisitCmdSeq(rule.ProcedureToMatchToInsertion[procSig]);
                if (impl.Blocks.Count > 0 && !QKeyValue.FindBoolAttribute(procSig.Attributes, ExprMatchVisitor.BoogieKeyWords.ReplaceImplementation))
                {
                    impl.Blocks.Insert(0,
                                       BoogieAstFactory.MkBlock(newCmds,
                                                                BoogieAstFactory.MkGotoCmd(impl.Blocks.First().Label)));
                }
                else
                {
                    impl.Blocks = new List <Block>();
                    impl.Blocks.Add(
                        BoogieAstFactory.MkBlock(newCmds));
                }
            }
        }
Exemplo n.º 4
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);
        }
        private static List <Block> splitBlockForSourceInfo(Block block)
        {
            var ret = new List <Block>();

            var curr  = new List <Cmd>();
            var label = block.Label;

            var origCmds     = block.Cmds;
            var origTransfer = block.TransferCmd;

            if (origCmds.Count == 0)
            {
                ret.Add(block);
                return(ret);
            }

            // Lets construct the list of cmds for the input block -- this is
            // the list of cmds up to (and excluding):
            //   -- The first cmd with source info that is not the 0^th cmd
            var cnt = 0;

            for (cnt = 0; cnt < origCmds.Count; cnt++)
            {
                if (hasSourceInfo(origCmds[cnt]) && cnt != 0)
                {
                    break;
                }
                curr.Add(origCmds[cnt]);
            }

            if (cnt == origCmds.Count)
            {
                ret.Add(block);
                return(ret);
            }

            block.Cmds        = curr;
            label             = getNewLabel();
            block.TransferCmd = BoogieAstFactory.MkGotoCmd(label);
            ret.Add(block);

            curr = new List <Cmd>();

            // Now for the rest of the cmds
            while (cnt < origCmds.Count)
            {
                Debug.Assert(hasSourceInfo(origCmds[cnt]));
                curr.Add(origCmds[cnt]);
                cnt++;

                for (; cnt < origCmds.Count; cnt++)
                {
                    if (hasSourceInfo(origCmds[cnt]))
                    {
                        break;
                    }
                    curr.Add(origCmds[cnt]);
                }
                var next = getNewLabel();
                ret.Add(new Block(Token.NoToken, label, curr, BoogieAstFactory.MkGotoCmd(next)));

                label = next;
                curr  = new List <Cmd>();
            }

            ret.Last().TransferCmd = origTransfer;

            return(ret);
        }
Exemplo n.º 6
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);
        }
Exemplo n.º 7
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.º 8
0
        // If the impl has multiple calls:
        //   "call foo(args); return;"
        // with the same args, then merge these calls into one
        public static string mergeRecCalls(Implementation impl)
        {
            // find the recursive calls
            var rBlocks = new List <Block>();

            foreach (var blk in impl.Blocks)
            {
                var rc =
                    blk.Cmds
                    .OfType <CallCmd>()
                    .Where(cc => cc.callee == impl.Name);
                if (!rc.Any())
                {
                    continue;
                }
                if (rc.Count() != 1)
                {
                    return(null);
                }

                // make sure recursive call is last in the block
                var rcall = rc.First();
                if (rcall != blk.Cmds.Last())
                {
                    return(null);
                }

                // check return
                if (!(blk.TransferCmd is ReturnCmd))
                {
                    return(null);
                }

                rBlocks.Add(blk);
            }

            if (rBlocks.Count <= 1)
            {
                return(null);
            }

            // grab the rec calls
            var recCalls = new Dictionary <string, CallCmd>();

            rBlocks.Iter(blk => recCalls.Add(blk.Label, blk.Cmds.Last() as CallCmd));

            // prune attributes
            var origAttr = new Dictionary <string, QKeyValue>();

            recCalls.Iter(kvp => origAttr.Add(kvp.Key, kvp.Value.Attributes));

            recCalls.Values
            .Iter(cc => cc.Attributes = BoogieUtil.removeAttrs(new HashSet <string> {
                "si_unique_call", "si_old_unique_call"
            }, cc.Attributes));

            // check that all recursive calls have the same arguments

            // Check 1: ToString
            var callStr = new HashSet <string>();

            recCalls.Values
            .Iter(cc =>
            {
                var str = new System.IO.StringWriter();
                var tt  = new TokenTextWriter(str);
                cc.Emit(tt, 0);
                tt.Close();
                callStr.Add(str.ToString());
                str.Close();
            });

            if (callStr.Count != 1)
            {
                // restore attributes
                recCalls
                .Iter(kvp => kvp.Value.Attributes = origAttr[kvp.Key]);
                return(null);
            }

            // Check 2: AST
            var rc1 = recCalls[recCalls.Keys.First()];

            if (
                recCalls.Values
                .Where(c => !IsSame(rc1, c))
                .Any())
            {
                // restore attributes
                recCalls
                .Iter(kvp => kvp.Value.Attributes = origAttr[kvp.Key]);

                return(null);
            }

            // Merge
            rc1.Attributes = origAttr[recCalls.Keys.First()];
            var nb = BoogieAstFactory.MkBlock(rc1);

            rBlocks.Iter(blk => blk.Cmds.Remove(blk.Cmds.Last()));
            rBlocks.Iter(blk =>
            {
                var gc          = BoogieAstFactory.MkGotoCmd(nb.Label);
                gc.labelTargets = new List <Block>();
                gc.labelTargets.Add(nb);
                blk.TransferCmd = gc;
            });
            impl.Blocks.Add(nb);
            return(nb.Label);
        }