1b4ad28daSMomchil Velikov //===------ CFIFixup.cpp - Insert CFI remember/restore instructions -------===// 2b4ad28daSMomchil Velikov // 3b4ad28daSMomchil Velikov // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4b4ad28daSMomchil Velikov // See https://llvm.org/LICENSE.txt for license information. 5b4ad28daSMomchil Velikov // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6b4ad28daSMomchil Velikov // 7b4ad28daSMomchil Velikov //===----------------------------------------------------------------------===// 8b4ad28daSMomchil Velikov // 9b4ad28daSMomchil Velikov 10b4ad28daSMomchil Velikov // This pass inserts the necessary instructions to adjust for the inconsistency 11b4ad28daSMomchil Velikov // of the call-frame information caused by final machine basic block layout. 12b4ad28daSMomchil Velikov // The pass relies in constraints LLVM imposes on the placement of 1333374c44SMomchil Velikov // save/restore points (cf. ShrinkWrap) and has certain preconditions about 1433374c44SMomchil Velikov // placement of CFI instructions: 1533374c44SMomchil Velikov // * For any two CFI instructions of the function prologue one dominates 1633374c44SMomchil Velikov // and is post-dominated by the other. 1733374c44SMomchil Velikov // * The function possibly contains multiple epilogue blocks, where each 1833374c44SMomchil Velikov // epilogue block is complete and self-contained, i.e. CSR restore 1933374c44SMomchil Velikov // instructions (and the corresponding CFI instructions) 2033374c44SMomchil Velikov // are not split across two or more blocks. 2133374c44SMomchil Velikov // * CFI instructions are not contained in any loops. 2233374c44SMomchil Velikov 2333374c44SMomchil Velikov // Thus, during execution, at the beginning and at the end of each basic block, 2433374c44SMomchil Velikov // following the prologue, the function can be in one of two states: 25b4ad28daSMomchil Velikov // - "has a call frame", if the function has executed the prologue, and 26b4ad28daSMomchil Velikov // has not executed any epilogue 27b4ad28daSMomchil Velikov // - "does not have a call frame", if the function has not executed the 28b4ad28daSMomchil Velikov // prologue, or has executed an epilogue 29b4ad28daSMomchil Velikov // which can be computed by a single RPO traversal. 30b4ad28daSMomchil Velikov 3133374c44SMomchil Velikov // The location of the prologue is determined by finding the first block in the 3233374c44SMomchil Velikov // reverse traversal which contains CFI instructions. 3333374c44SMomchil Velikov 34b4ad28daSMomchil Velikov // In order to accommodate backends which do not generate unwind info in 35b4ad28daSMomchil Velikov // epilogues we compute an additional property "strong no call frame on entry", 36b4ad28daSMomchil Velikov // which is set for the entry point of the function and for every block 37b4ad28daSMomchil Velikov // reachable from the entry along a path that does not execute the prologue. If 38b4ad28daSMomchil Velikov // this property holds, it takes precedence over the "has a call frame" 39b4ad28daSMomchil Velikov // property. 40b4ad28daSMomchil Velikov 41b4ad28daSMomchil Velikov // From the point of view of the unwind tables, the "has/does not have call 42b4ad28daSMomchil Velikov // frame" state at beginning of each block is determined by the state at the end 43b4ad28daSMomchil Velikov // of the previous block, in layout order. Where these states differ, we insert 44b4ad28daSMomchil Velikov // compensating CFI instructions, which come in two flavours: 45b4ad28daSMomchil Velikov 46b4ad28daSMomchil Velikov // - CFI instructions, which reset the unwind table state to the initial one. 47b4ad28daSMomchil Velikov // This is done by a target specific hook and is expected to be trivial 48b4ad28daSMomchil Velikov // to implement, for example it could be: 49b4ad28daSMomchil Velikov // .cfi_def_cfa <sp>, 0 50b4ad28daSMomchil Velikov // .cfi_same_value <rN> 51b4ad28daSMomchil Velikov // .cfi_same_value <rN-1> 52b4ad28daSMomchil Velikov // ... 53b4ad28daSMomchil Velikov // where <rN> are the callee-saved registers. 54b4ad28daSMomchil Velikov // - CFI instructions, which reset the unwind table state to the one 55b4ad28daSMomchil Velikov // created by the function prologue. These are 56b4ad28daSMomchil Velikov // .cfi_restore_state 57b4ad28daSMomchil Velikov // .cfi_remember_state 58b4ad28daSMomchil Velikov // In this case we also insert a `.cfi_remember_state` after the last CFI 59b4ad28daSMomchil Velikov // instruction in the function prologue. 60b4ad28daSMomchil Velikov // 61b4ad28daSMomchil Velikov // Known limitations: 62b4ad28daSMomchil Velikov // * the pass cannot handle an epilogue preceding the prologue in the basic 63b4ad28daSMomchil Velikov // block layout 64b4ad28daSMomchil Velikov // * the pass does not handle functions where SP is used as a frame pointer and 65b4ad28daSMomchil Velikov // SP adjustments up and down are done in different basic blocks (TODO) 66b4ad28daSMomchil Velikov //===----------------------------------------------------------------------===// 67b4ad28daSMomchil Velikov 68b4ad28daSMomchil Velikov #include "llvm/CodeGen/CFIFixup.h" 69b4ad28daSMomchil Velikov 70*07137ce3SDaniel Hoekwater #include "llvm/ADT/DenseMap.h" 71b4ad28daSMomchil Velikov #include "llvm/ADT/PostOrderIterator.h" 72*07137ce3SDaniel Hoekwater #include "llvm/ADT/STLExtras.h" 73*07137ce3SDaniel Hoekwater #include "llvm/ADT/iterator_range.h" 74*07137ce3SDaniel Hoekwater #include "llvm/CodeGen/MachineBasicBlock.h" 75*07137ce3SDaniel Hoekwater #include "llvm/CodeGen/MachineFunction.h" 76b4ad28daSMomchil Velikov #include "llvm/CodeGen/Passes.h" 77b4ad28daSMomchil Velikov #include "llvm/CodeGen/TargetFrameLowering.h" 78b4ad28daSMomchil Velikov #include "llvm/CodeGen/TargetInstrInfo.h" 79b4ad28daSMomchil Velikov #include "llvm/CodeGen/TargetSubtargetInfo.h" 80b4ad28daSMomchil Velikov #include "llvm/MC/MCAsmInfo.h" 81b4ad28daSMomchil Velikov #include "llvm/MC/MCDwarf.h" 82b4ad28daSMomchil Velikov #include "llvm/Target/TargetMachine.h" 83b4ad28daSMomchil Velikov 84*07137ce3SDaniel Hoekwater #include <iterator> 85*07137ce3SDaniel Hoekwater 86b4ad28daSMomchil Velikov using namespace llvm; 87b4ad28daSMomchil Velikov 88b4ad28daSMomchil Velikov #define DEBUG_TYPE "cfi-fixup" 89b4ad28daSMomchil Velikov 90b4ad28daSMomchil Velikov char CFIFixup::ID = 0; 91b4ad28daSMomchil Velikov 92b4ad28daSMomchil Velikov INITIALIZE_PASS(CFIFixup, "cfi-fixup", 93b4ad28daSMomchil Velikov "Insert CFI remember/restore state instructions", false, false) 94b4ad28daSMomchil Velikov FunctionPass *llvm::createCFIFixup() { return new CFIFixup(); } 95b4ad28daSMomchil Velikov 96b4ad28daSMomchil Velikov static bool isPrologueCFIInstruction(const MachineInstr &MI) { 97b4ad28daSMomchil Velikov return MI.getOpcode() == TargetOpcode::CFI_INSTRUCTION && 98b4ad28daSMomchil Velikov MI.getFlag(MachineInstr::FrameSetup); 99b4ad28daSMomchil Velikov } 100b4ad28daSMomchil Velikov 101b4ad28daSMomchil Velikov static bool containsEpilogue(const MachineBasicBlock &MBB) { 102b4ad28daSMomchil Velikov return llvm::any_of(llvm::reverse(MBB), [](const auto &MI) { 103b4ad28daSMomchil Velikov return MI.getOpcode() == TargetOpcode::CFI_INSTRUCTION && 104b4ad28daSMomchil Velikov MI.getFlag(MachineInstr::FrameDestroy); 105b4ad28daSMomchil Velikov }); 106b4ad28daSMomchil Velikov } 107b4ad28daSMomchil Velikov 10833374c44SMomchil Velikov static MachineBasicBlock * 10933374c44SMomchil Velikov findPrologueEnd(MachineFunction &MF, MachineBasicBlock::iterator &PrologueEnd) { 11033374c44SMomchil Velikov // Even though we should theoretically traverse the blocks in post-order, we 11133374c44SMomchil Velikov // can't encode correctly cases where prologue blocks are not laid out in 11233374c44SMomchil Velikov // topological order. Then, assuming topological order, we can just traverse 11333374c44SMomchil Velikov // the function in reverse. 11433374c44SMomchil Velikov for (MachineBasicBlock &MBB : reverse(MF)) { 11533374c44SMomchil Velikov for (MachineInstr &MI : reverse(MBB.instrs())) { 11633374c44SMomchil Velikov if (!isPrologueCFIInstruction(MI)) 11733374c44SMomchil Velikov continue; 11833374c44SMomchil Velikov PrologueEnd = std::next(MI.getIterator()); 11933374c44SMomchil Velikov return &MBB; 12033374c44SMomchil Velikov } 12133374c44SMomchil Velikov } 12233374c44SMomchil Velikov return nullptr; 12333374c44SMomchil Velikov } 12433374c44SMomchil Velikov 1251b8cff9aSDaniel Hoekwater // Represents the point within a basic block where we can insert an instruction. 1261b8cff9aSDaniel Hoekwater // Note that we need the MachineBasicBlock* as well as the iterator since the 1271b8cff9aSDaniel Hoekwater // iterator can point to the end of the block. Instructions are inserted 1281b8cff9aSDaniel Hoekwater // *before* the iterator. 1291b8cff9aSDaniel Hoekwater struct InsertionPoint { 130*07137ce3SDaniel Hoekwater MachineBasicBlock *MBB = nullptr; 1311b8cff9aSDaniel Hoekwater MachineBasicBlock::iterator Iterator; 1321b8cff9aSDaniel Hoekwater }; 1331b8cff9aSDaniel Hoekwater 1341b8cff9aSDaniel Hoekwater // Inserts a `.cfi_remember_state` instruction before PrologueEnd and a 1351b8cff9aSDaniel Hoekwater // `.cfi_restore_state` instruction before DstInsertPt. Returns an iterator 1361b8cff9aSDaniel Hoekwater // to the first instruction after the inserted `.cfi_restore_state` instruction. 1371b8cff9aSDaniel Hoekwater static InsertionPoint 1381b8cff9aSDaniel Hoekwater insertRememberRestorePair(const InsertionPoint &RememberInsertPt, 1391b8cff9aSDaniel Hoekwater const InsertionPoint &RestoreInsertPt) { 1401b8cff9aSDaniel Hoekwater MachineFunction &MF = *RememberInsertPt.MBB->getParent(); 1411b8cff9aSDaniel Hoekwater const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo(); 1421b8cff9aSDaniel Hoekwater 1431b8cff9aSDaniel Hoekwater // Insert the `.cfi_remember_state` instruction. 1441b8cff9aSDaniel Hoekwater unsigned CFIIndex = 1451b8cff9aSDaniel Hoekwater MF.addFrameInst(MCCFIInstruction::createRememberState(nullptr)); 1461b8cff9aSDaniel Hoekwater BuildMI(*RememberInsertPt.MBB, RememberInsertPt.Iterator, DebugLoc(), 1471b8cff9aSDaniel Hoekwater TII.get(TargetOpcode::CFI_INSTRUCTION)) 1481b8cff9aSDaniel Hoekwater .addCFIIndex(CFIIndex); 1491b8cff9aSDaniel Hoekwater 1501b8cff9aSDaniel Hoekwater // Insert the `.cfi_restore_state` instruction. 1511b8cff9aSDaniel Hoekwater CFIIndex = MF.addFrameInst(MCCFIInstruction::createRestoreState(nullptr)); 1521b8cff9aSDaniel Hoekwater 1531b8cff9aSDaniel Hoekwater return {RestoreInsertPt.MBB, 1541b8cff9aSDaniel Hoekwater std::next(BuildMI(*RestoreInsertPt.MBB, RestoreInsertPt.Iterator, 1551b8cff9aSDaniel Hoekwater DebugLoc(), TII.get(TargetOpcode::CFI_INSTRUCTION)) 1561b8cff9aSDaniel Hoekwater .addCFIIndex(CFIIndex) 1571b8cff9aSDaniel Hoekwater ->getIterator())}; 1581b8cff9aSDaniel Hoekwater } 1591b8cff9aSDaniel Hoekwater 160*07137ce3SDaniel Hoekwater // Copies all CFI instructions before PrologueEnd and inserts them before 161*07137ce3SDaniel Hoekwater // DstInsertPt. Returns the iterator to the first instruction after the 162*07137ce3SDaniel Hoekwater // inserted instructions. 163*07137ce3SDaniel Hoekwater static InsertionPoint cloneCfiPrologue(const InsertionPoint &PrologueEnd, 164*07137ce3SDaniel Hoekwater const InsertionPoint &DstInsertPt) { 165*07137ce3SDaniel Hoekwater MachineFunction &MF = *DstInsertPt.MBB->getParent(); 166*07137ce3SDaniel Hoekwater 167*07137ce3SDaniel Hoekwater auto cloneCfiInstructions = [&](MachineBasicBlock::iterator Begin, 168*07137ce3SDaniel Hoekwater MachineBasicBlock::iterator End) { 169*07137ce3SDaniel Hoekwater auto ToClone = map_range( 170*07137ce3SDaniel Hoekwater make_filter_range(make_range(Begin, End), isPrologueCFIInstruction), 171*07137ce3SDaniel Hoekwater [&](const MachineInstr &MI) { return MF.CloneMachineInstr(&MI); }); 172*07137ce3SDaniel Hoekwater DstInsertPt.MBB->insert(DstInsertPt.Iterator, ToClone.begin(), 173*07137ce3SDaniel Hoekwater ToClone.end()); 174*07137ce3SDaniel Hoekwater }; 175*07137ce3SDaniel Hoekwater 176*07137ce3SDaniel Hoekwater // Clone all CFI instructions from previous blocks. 177*07137ce3SDaniel Hoekwater for (auto &MBB : make_range(MF.begin(), PrologueEnd.MBB->getIterator())) 178*07137ce3SDaniel Hoekwater cloneCfiInstructions(MBB.begin(), MBB.end()); 179*07137ce3SDaniel Hoekwater // Clone all CFI instructions from the final prologue block. 180*07137ce3SDaniel Hoekwater cloneCfiInstructions(PrologueEnd.MBB->begin(), PrologueEnd.Iterator); 181*07137ce3SDaniel Hoekwater return DstInsertPt; 182*07137ce3SDaniel Hoekwater } 183*07137ce3SDaniel Hoekwater 184b4ad28daSMomchil Velikov bool CFIFixup::runOnMachineFunction(MachineFunction &MF) { 185b4ad28daSMomchil Velikov const TargetFrameLowering &TFL = *MF.getSubtarget().getFrameLowering(); 186b4ad28daSMomchil Velikov if (!TFL.enableCFIFixup(MF)) 187b4ad28daSMomchil Velikov return false; 188b4ad28daSMomchil Velikov 189b4ad28daSMomchil Velikov const unsigned NumBlocks = MF.getNumBlockIDs(); 190b4ad28daSMomchil Velikov if (NumBlocks < 2) 191b4ad28daSMomchil Velikov return false; 192b4ad28daSMomchil Velikov 19333374c44SMomchil Velikov // Find the prologue and the point where we can issue the first 19433374c44SMomchil Velikov // `.cfi_remember_state`. 19533374c44SMomchil Velikov MachineBasicBlock::iterator PrologueEnd; 19633374c44SMomchil Velikov MachineBasicBlock *PrologueBlock = findPrologueEnd(MF, PrologueEnd); 19733374c44SMomchil Velikov if (PrologueBlock == nullptr) 19833374c44SMomchil Velikov return false; 19933374c44SMomchil Velikov 200b4ad28daSMomchil Velikov struct BlockFlags { 201b4ad28daSMomchil Velikov bool Reachable : 1; 202b4ad28daSMomchil Velikov bool StrongNoFrameOnEntry : 1; 203b4ad28daSMomchil Velikov bool HasFrameOnEntry : 1; 204b4ad28daSMomchil Velikov bool HasFrameOnExit : 1; 205b4ad28daSMomchil Velikov }; 206*07137ce3SDaniel Hoekwater SmallVector<BlockFlags, 32> BlockInfo(NumBlocks, 207*07137ce3SDaniel Hoekwater {false, false, false, false}); 208b4ad28daSMomchil Velikov BlockInfo[0].Reachable = true; 209b4ad28daSMomchil Velikov BlockInfo[0].StrongNoFrameOnEntry = true; 210b4ad28daSMomchil Velikov 211b4ad28daSMomchil Velikov // Compute the presence/absence of frame at each basic block. 212b4ad28daSMomchil Velikov ReversePostOrderTraversal<MachineBasicBlock *> RPOT(&*MF.begin()); 213b4ad28daSMomchil Velikov for (MachineBasicBlock *MBB : RPOT) { 214b4ad28daSMomchil Velikov BlockFlags &Info = BlockInfo[MBB->getNumber()]; 215b4ad28daSMomchil Velikov 216b4ad28daSMomchil Velikov // Set to true if the current block contains the prologue or the epilogue, 217b4ad28daSMomchil Velikov // respectively. 21833374c44SMomchil Velikov bool HasPrologue = MBB == PrologueBlock; 219b4ad28daSMomchil Velikov bool HasEpilogue = false; 220b4ad28daSMomchil Velikov 221b4ad28daSMomchil Velikov if (Info.HasFrameOnEntry || HasPrologue) 222b4ad28daSMomchil Velikov HasEpilogue = containsEpilogue(*MBB); 223b4ad28daSMomchil Velikov 224b4ad28daSMomchil Velikov // If the function has a call frame at the entry of the current block or the 225b4ad28daSMomchil Velikov // current block contains the prologue, then the function has a call frame 226b4ad28daSMomchil Velikov // at the exit of the block, unless the block contains the epilogue. 227b4ad28daSMomchil Velikov Info.HasFrameOnExit = (Info.HasFrameOnEntry || HasPrologue) && !HasEpilogue; 228b4ad28daSMomchil Velikov 229b4ad28daSMomchil Velikov // Set the successors' state on entry. 230b4ad28daSMomchil Velikov for (MachineBasicBlock *Succ : MBB->successors()) { 231b4ad28daSMomchil Velikov BlockFlags &SuccInfo = BlockInfo[Succ->getNumber()]; 232b4ad28daSMomchil Velikov SuccInfo.Reachable = true; 233b4ad28daSMomchil Velikov SuccInfo.StrongNoFrameOnEntry |= 234b4ad28daSMomchil Velikov Info.StrongNoFrameOnEntry && !HasPrologue; 235b4ad28daSMomchil Velikov SuccInfo.HasFrameOnEntry = Info.HasFrameOnExit; 236b4ad28daSMomchil Velikov } 237b4ad28daSMomchil Velikov } 238b4ad28daSMomchil Velikov 239b4ad28daSMomchil Velikov // Walk the blocks of the function in "physical" order. 240b4ad28daSMomchil Velikov // Every block inherits the frame state (as recorded in the unwind tables) 241b4ad28daSMomchil Velikov // of the previous block. If the intended frame state is different, insert 242b4ad28daSMomchil Velikov // compensating CFI instructions. 243b4ad28daSMomchil Velikov bool Change = false; 244*07137ce3SDaniel Hoekwater // `InsertPt[sectionID]` always points to the point in a preceding block where 245*07137ce3SDaniel Hoekwater // we have to insert a `.cfi_remember_state`, in the case that the current 246*07137ce3SDaniel Hoekwater // block needs a `.cfi_restore_state`. 247*07137ce3SDaniel Hoekwater SmallDenseMap<MBBSectionID, InsertionPoint> InsertionPts; 248*07137ce3SDaniel Hoekwater InsertionPts[PrologueBlock->getSectionID()] = {PrologueBlock, PrologueEnd}; 249b4ad28daSMomchil Velikov 2501b8cff9aSDaniel Hoekwater assert(PrologueEnd != PrologueBlock->begin() && 251b4ad28daSMomchil Velikov "Inconsistent notion of \"prologue block\""); 252b4ad28daSMomchil Velikov 253b4ad28daSMomchil Velikov // No point starting before the prologue block. 254b4ad28daSMomchil Velikov // TODO: the unwind tables will still be incorrect if an epilogue physically 255b4ad28daSMomchil Velikov // preceeds the prologue. 256b4ad28daSMomchil Velikov MachineFunction::iterator CurrBB = std::next(PrologueBlock->getIterator()); 257b4ad28daSMomchil Velikov bool HasFrame = BlockInfo[PrologueBlock->getNumber()].HasFrameOnExit; 258b4ad28daSMomchil Velikov while (CurrBB != MF.end()) { 259b4ad28daSMomchil Velikov const BlockFlags &Info = BlockInfo[CurrBB->getNumber()]; 260b4ad28daSMomchil Velikov if (!Info.Reachable) { 261b4ad28daSMomchil Velikov ++CurrBB; 262b4ad28daSMomchil Velikov continue; 263b4ad28daSMomchil Velikov } 264b4ad28daSMomchil Velikov 265b4ad28daSMomchil Velikov #ifndef NDEBUG 266b4ad28daSMomchil Velikov if (!Info.StrongNoFrameOnEntry) { 267b4ad28daSMomchil Velikov for (auto *Pred : CurrBB->predecessors()) { 268b4ad28daSMomchil Velikov BlockFlags &PredInfo = BlockInfo[Pred->getNumber()]; 269b4ad28daSMomchil Velikov assert((!PredInfo.Reachable || 270b4ad28daSMomchil Velikov Info.HasFrameOnEntry == PredInfo.HasFrameOnExit) && 271b4ad28daSMomchil Velikov "Inconsistent call frame state"); 272b4ad28daSMomchil Velikov } 273b4ad28daSMomchil Velikov } 274b4ad28daSMomchil Velikov #endif 275*07137ce3SDaniel Hoekwater 276*07137ce3SDaniel Hoekwater // If the block is the first block in its section, then it doesn't have a 277*07137ce3SDaniel Hoekwater // frame on entry. 278*07137ce3SDaniel Hoekwater HasFrame &= !CurrBB->isBeginSection(); 279b4ad28daSMomchil Velikov if (!Info.StrongNoFrameOnEntry && Info.HasFrameOnEntry && !HasFrame) { 280b4ad28daSMomchil Velikov // Reset to the "after prologue" state. 281b4ad28daSMomchil Velikov 282*07137ce3SDaniel Hoekwater InsertionPoint &InsertPt = InsertionPts[CurrBB->getSectionID()]; 283*07137ce3SDaniel Hoekwater if (InsertPt.MBB == nullptr) { 284*07137ce3SDaniel Hoekwater // CurBB is the first block in its section, so there is no "after 285*07137ce3SDaniel Hoekwater // prologue" state. Clone the CFI instructions from the prologue block 286*07137ce3SDaniel Hoekwater // to create it. 287*07137ce3SDaniel Hoekwater InsertPt = cloneCfiPrologue({PrologueBlock, PrologueEnd}, 288*07137ce3SDaniel Hoekwater {&*CurrBB, CurrBB->begin()}); 289*07137ce3SDaniel Hoekwater } else { 2901b8cff9aSDaniel Hoekwater // There's an earlier block known to have a stack frame. Insert a 2911b8cff9aSDaniel Hoekwater // `.cfi_remember_state` instruction into that block and a 292*07137ce3SDaniel Hoekwater // `.cfi_restore_state` instruction at the beginning of the current 293*07137ce3SDaniel Hoekwater // block. 294*07137ce3SDaniel Hoekwater InsertPt = 295*07137ce3SDaniel Hoekwater insertRememberRestorePair(InsertPt, {&*CurrBB, CurrBB->begin()}); 296*07137ce3SDaniel Hoekwater } 297b4ad28daSMomchil Velikov Change = true; 298b4ad28daSMomchil Velikov } else if ((Info.StrongNoFrameOnEntry || !Info.HasFrameOnEntry) && 299b4ad28daSMomchil Velikov HasFrame) { 300b4ad28daSMomchil Velikov // Reset to the state upon function entry. 301b4ad28daSMomchil Velikov TFL.resetCFIToInitialState(*CurrBB); 302b4ad28daSMomchil Velikov Change = true; 303b4ad28daSMomchil Velikov } 304b4ad28daSMomchil Velikov 305b4ad28daSMomchil Velikov HasFrame = Info.HasFrameOnExit; 306b4ad28daSMomchil Velikov ++CurrBB; 307b4ad28daSMomchil Velikov } 308b4ad28daSMomchil Velikov 309b4ad28daSMomchil Velikov return Change; 310b4ad28daSMomchil Velikov } 311