1 //===- bolt/Passes/ShrinkWrapping.h -----------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifndef BOLT_PASSES_SHRINKWRAPPING_H 10 #define BOLT_PASSES_SHRINKWRAPPING_H 11 12 #include "bolt/Passes/FrameAnalysis.h" 13 #include "llvm/MC/MCRegisterInfo.h" 14 #include <atomic> 15 16 namespace llvm { 17 namespace bolt { 18 class DataflowInfoManager; 19 20 /// Encapsulates logic required to analyze a binary function and detect which 21 /// registers are being saved as callee-saved, where are these saves and where 22 /// are the points where their original value are being restored. 23 class CalleeSavedAnalysis { 24 const FrameAnalysis &FA; 25 const BinaryContext &BC; 26 BinaryFunction &BF; 27 DataflowInfoManager &Info; 28 MCPlusBuilder::AllocatorIdTy AllocatorId; 29 30 std::optional<unsigned> SaveTagIndex; 31 std::optional<unsigned> RestoreTagIndex; 32 33 /// Compute all stores of callee-saved regs. Those are the ones that stores a 34 /// register whose definition is not local. 35 void analyzeSaves(); 36 37 /// Similar to analyzeSaves, tries to determine all instructions that recover 38 /// the original value of the callee-saved register before exiting the 39 /// function. 40 void analyzeRestores(); 41 getSaveTag()42 unsigned getSaveTag() { 43 if (SaveTagIndex) 44 return *SaveTagIndex; 45 SaveTagIndex = BC.MIB->getOrCreateAnnotationIndex(getSaveTagName()); 46 return *SaveTagIndex; 47 } 48 getRestoreTag()49 unsigned getRestoreTag() { 50 if (RestoreTagIndex) 51 return *RestoreTagIndex; 52 RestoreTagIndex = BC.MIB->getOrCreateAnnotationIndex(getRestoreTagName()); 53 return *RestoreTagIndex; 54 } 55 56 public: 57 BitVector CalleeSaved; 58 std::vector<int64_t> OffsetsByReg; 59 BitVector HasRestores; 60 std::vector<uint64_t> SavingCost; 61 std::vector<const FrameIndexEntry *> SaveFIEByReg; 62 std::vector<const FrameIndexEntry *> LoadFIEByReg; 63 CalleeSavedAnalysis(const FrameAnalysis & FA,BinaryFunction & BF,DataflowInfoManager & Info,MCPlusBuilder::AllocatorIdTy AllocId)64 CalleeSavedAnalysis(const FrameAnalysis &FA, BinaryFunction &BF, 65 DataflowInfoManager &Info, 66 MCPlusBuilder::AllocatorIdTy AllocId) 67 : FA(FA), BC(BF.getBinaryContext()), BF(BF), Info(Info), 68 AllocatorId(AllocId), CalleeSaved(BC.MRI->getNumRegs(), false), 69 OffsetsByReg(BC.MRI->getNumRegs(), 0LL), 70 HasRestores(BC.MRI->getNumRegs(), false), 71 SavingCost(BC.MRI->getNumRegs(), 0ULL), 72 SaveFIEByReg(BC.MRI->getNumRegs(), nullptr), 73 LoadFIEByReg(BC.MRI->getNumRegs(), nullptr) {} 74 75 ~CalleeSavedAnalysis(); 76 compute()77 void compute() { 78 analyzeSaves(); 79 analyzeRestores(); 80 } 81 82 /// Retrieves the value of the callee-saved register that is saved by this 83 /// instruction or 0 if this is not a CSR save instruction. getSavedReg(const MCInst & Inst)84 uint16_t getSavedReg(const MCInst &Inst) { 85 auto Val = BC.MIB->tryGetAnnotationAs<decltype(FrameIndexEntry::RegOrImm)>( 86 Inst, getSaveTag()); 87 if (Val) 88 return *Val; 89 return 0; 90 } 91 92 /// Retrieves the value of the callee-saved register that is restored by this 93 /// instruction or 0 if this is not a CSR restore instruction. getRestoredReg(const MCInst & Inst)94 uint16_t getRestoredReg(const MCInst &Inst) { 95 auto Val = BC.MIB->tryGetAnnotationAs<decltype(FrameIndexEntry::RegOrImm)>( 96 Inst, getRestoreTag()); 97 if (Val) 98 return *Val; 99 return 0; 100 } 101 102 /// Routines to compute all saves/restores for a Reg (needs to traverse all 103 /// instructions). 104 std::vector<MCInst *> getSavesByReg(uint16_t Reg); 105 std::vector<MCInst *> getRestoresByReg(uint16_t Reg); 106 107 /// Returns the identifying string used to annotate instructions with metadata 108 /// for this analysis. These are deleted in the destructor. getSaveTagName()109 static StringRef getSaveTagName() { return StringRef("CSA-SavedReg"); } 110 getRestoreTagName()111 static StringRef getRestoreTagName() { return StringRef("CSA-RestoredReg"); } 112 }; 113 114 /// Identifies in a given binary function all stack regions being used and allow 115 /// us to edit the layout, removing or inserting new regions. When the layout is 116 /// modified, all affected stack-accessing instructions are updated. 117 class StackLayoutModifier { 118 const FrameAnalysis &FA; 119 const BinaryContext &BC; 120 BinaryFunction &BF; 121 DataflowInfoManager &Info; 122 MCPlusBuilder::AllocatorIdTy AllocatorId; 123 124 // Keep track of stack slots we know how to safely move 125 std::map<int64_t, int64_t> AvailableRegions; 126 127 DenseSet<int64_t> CollapsedRegions; 128 DenseSet<int64_t> InsertedRegions; 129 130 // A map of chunks of stack memory we don't really know what's happening there 131 // and we need to leave it untouched. 132 std::map<int64_t, int64_t> BlacklistedRegions; 133 134 // Maps stack slots to the regs that are saved to them 135 DenseMap<int64_t, std::set<MCPhysReg>> RegionToRegMap; 136 DenseMap<int, std::set<int64_t>> RegToRegionMap; 137 138 // If we can't understand how to move stack slots, IsSimple will be false 139 bool IsSimple{true}; 140 141 bool IsInitialized{false}; 142 143 std::optional<unsigned> TodoTagIndex; 144 std::optional<unsigned> SlotTagIndex; 145 std::optional<unsigned> OffsetCFIRegTagIndex; 146 147 public: 148 // Keep a worklist of operations to perform on the function to perform 149 // the requested layout modifications via collapseRegion()/insertRegion(). 150 struct WorklistItem { 151 enum ActionType : uint8_t { 152 None = 0, 153 AdjustLoadStoreOffset, 154 AdjustCFI, 155 } Action; 156 157 int64_t OffsetUpdate{0}; WorklistItemWorklistItem158 WorklistItem() : Action(None) {} WorklistItemWorklistItem159 WorklistItem(ActionType Action) : Action(Action) {} WorklistItemWorklistItem160 WorklistItem(ActionType Action, int OffsetUpdate) 161 : Action(Action), OffsetUpdate(OffsetUpdate) {} 162 }; 163 164 private: 165 /// Mark the stack region identified by \p Offset and \p Size to be a 166 /// no-touch zone, whose accesses cannot be relocated to another region. 167 void blacklistRegion(int64_t Offset, int64_t Size); 168 169 /// Check if this region overlaps with blacklisted addresses 170 bool isRegionBlacklisted(int64_t Offset, int64_t Size); 171 172 /// Check if the region identified by \p Offset and \p Size has any conflicts 173 /// with available regions so far. If it has, blacklist all involved regions 174 /// and return true. 175 bool blacklistAllInConflictWith(int64_t Offset, int64_t Size); 176 177 /// If \p Point is identified as frame pointer initialization (defining the 178 /// value of FP with SP), check for non-standard initialization that precludes 179 /// us from changing the stack layout. If positive, update blacklisted 180 /// regions. 181 void checkFramePointerInitialization(MCInst &Point); 182 183 /// If \p Point is restoring the value with SP with FP plus offset, 184 /// add a slottag to this instruction as it needs to be updated when we 185 /// change the stack layout. 186 void checkStackPointerRestore(MCInst &Point); 187 188 /// Make sense of each stack offsets we can freely change 189 void classifyStackAccesses(); 190 void classifyCFIs(); 191 192 /// Used to keep track of modifications to the function that will later be 193 /// performed by performChanges(); 194 void scheduleChange(MCInst &Inst, WorklistItem Item); 195 getTodoTag()196 unsigned getTodoTag() { 197 if (TodoTagIndex) 198 return *TodoTagIndex; 199 TodoTagIndex = BC.MIB->getOrCreateAnnotationIndex(getTodoTagName()); 200 return *TodoTagIndex; 201 } 202 getSlotTag()203 unsigned getSlotTag() { 204 if (SlotTagIndex) 205 return *SlotTagIndex; 206 SlotTagIndex = BC.MIB->getOrCreateAnnotationIndex(getSlotTagName()); 207 return *SlotTagIndex; 208 } 209 getOffsetCFIRegTag()210 unsigned getOffsetCFIRegTag() { 211 if (OffsetCFIRegTagIndex) 212 return *OffsetCFIRegTagIndex; 213 OffsetCFIRegTagIndex = 214 BC.MIB->getOrCreateAnnotationIndex(getOffsetCFIRegTagName()); 215 return *OffsetCFIRegTagIndex; 216 } 217 218 public: StackLayoutModifier(const FrameAnalysis & FA,BinaryFunction & BF,DataflowInfoManager & Info,MCPlusBuilder::AllocatorIdTy AllocId)219 StackLayoutModifier(const FrameAnalysis &FA, BinaryFunction &BF, 220 DataflowInfoManager &Info, 221 MCPlusBuilder::AllocatorIdTy AllocId) 222 : FA(FA), BC(BF.getBinaryContext()), BF(BF), Info(Info), 223 AllocatorId(AllocId) {} 224 ~StackLayoutModifier()225 ~StackLayoutModifier() { 226 for (BinaryBasicBlock &BB : BF) { 227 for (MCInst &Inst : BB) { 228 BC.MIB->removeAnnotation(Inst, getTodoTag()); 229 BC.MIB->removeAnnotation(Inst, getSlotTag()); 230 BC.MIB->removeAnnotation(Inst, getOffsetCFIRegTag()); 231 } 232 } 233 } 234 235 /// Retrieves the value of the callee-saved register that is restored by this 236 /// instruction or 0 if this is not a CSR restore instruction. getOffsetCFIReg(const MCInst & Inst)237 uint16_t getOffsetCFIReg(const MCInst &Inst) { 238 auto Val = BC.MIB->tryGetAnnotationAs<uint16_t>(Inst, getOffsetCFIRegTag()); 239 if (Val) 240 return *Val; 241 return 0; 242 } 243 244 /// Check if it is possible to delete the push instruction \p DeletedPush. 245 /// This involves collapsing the region accessed by this push and updating all 246 /// other instructions that access affected memory regions. Return true if we 247 /// can update this. 248 bool canCollapseRegion(int64_t RegionAddr); 249 bool canCollapseRegion(MCInst *DeletedPush); 250 251 /// Notify the layout manager that \p DeletedPush was deleted and that it 252 /// needs to update other affected stack-accessing instructions. 253 bool collapseRegion(MCInst *Alloc, int64_t RegionAddr, int64_t RegionSize); 254 bool collapseRegion(MCInst *DeletedPush); 255 256 /// Set the new stack address difference for load/store instructions that 257 /// referenced a stack location that was deleted via collapseRegion. 258 void setOffsetForCollapsedAccesses(int64_t NewOffset); 259 260 /// Check if it is possible to insert a push instruction at point \p P. 261 /// This involves inserting a new region in the stack, possibly affecting 262 /// instructions that access the frame. Return true if we can update them all. 263 bool canInsertRegion(ProgramPoint P); 264 265 /// Notify the layout manager that a new push instruction has been inserted 266 /// at point \p P and that it will need to update relevant instructions. 267 bool insertRegion(ProgramPoint P, int64_t RegionSz); 268 269 /// Perform all changes scheduled by collapseRegion()/insertRegion() 270 void performChanges(); 271 272 /// Perform initial assessment of the function trying to understand its stack 273 /// accesses. 274 void initialize(); 275 getTodoTagName()276 static StringRef getTodoTagName() { return StringRef("SLM-TodoTag"); } 277 getSlotTagName()278 static StringRef getSlotTagName() { return StringRef("SLM-SlotTag"); } 279 getOffsetCFIRegTagName()280 static StringRef getOffsetCFIRegTagName() { 281 return StringRef("SLM-OffsetCFIReg"); 282 } 283 }; 284 285 /// Implements a pass to optimize callee-saved register spills. These spills 286 /// typically happen at function prologue/epilogue. When these are hot basic 287 /// blocks, this pass will try to move these spills to cold blocks whenever 288 /// possible. 289 class ShrinkWrapping { 290 const FrameAnalysis &FA; 291 const BinaryContext &BC; 292 BinaryFunction &BF; 293 DataflowInfoManager &Info; 294 MCPlusBuilder::AllocatorIdTy AllocatorId; 295 StackLayoutModifier SLM; 296 /// For each CSR, store a vector of all CFI indexes deleted as a consequence 297 /// of moving this Callee-Saved Reg 298 DenseMap<unsigned, std::vector<uint32_t>> DeletedPushCFIs; 299 DenseMap<unsigned, std::vector<uint32_t>> DeletedPopCFIs; 300 BitVector HasDeletedOffsetCFIs; 301 SmallPtrSet<const MCCFIInstruction *, 16> UpdatedCFIs; 302 std::vector<BitVector> UsesByReg; 303 std::vector<int64_t> PushOffsetByReg; 304 std::vector<int64_t> PopOffsetByReg; 305 std::vector<MCPhysReg> DomOrder; 306 CalleeSavedAnalysis CSA; 307 std::vector<std::vector<uint64_t>> BestSaveCount; 308 std::vector<std::vector<MCInst *>> BestSavePos; 309 310 /// Pass stats 311 static std::atomic<std::uint64_t> SpillsMovedRegularMode; 312 static std::atomic<std::uint64_t> SpillsMovedPushPopMode; 313 static std::atomic<std::uint64_t> SpillsMovedDynamicCount; 314 static std::atomic<std::uint64_t> SpillsFailedDynamicCount; 315 static std::atomic<std::uint64_t> InstrDynamicCount; 316 static std::atomic<std::uint64_t> StoreDynamicCount; 317 318 std::optional<unsigned> AnnotationIndex; 319 320 /// Allow our custom worklist-sensitive analysis 321 /// PredictiveStackPointerTracking to access WorklistItem 322 public: 323 struct WorklistItem { 324 enum ActionType : uint8_t { 325 Erase = 0, 326 ChangeToAdjustment, 327 InsertLoadOrStore, 328 InsertPushOrPop 329 } Action; 330 FrameIndexEntry FIEToInsert; 331 unsigned AffectedReg; 332 int Adjustment{0}; WorklistItemWorklistItem333 WorklistItem(ActionType Action, unsigned AffectedReg) 334 : Action(Action), FIEToInsert(), AffectedReg(AffectedReg) {} WorklistItemWorklistItem335 WorklistItem(ActionType Action, unsigned AffectedReg, int Adjustment) 336 : Action(Action), FIEToInsert(), AffectedReg(AffectedReg), 337 Adjustment(Adjustment) {} WorklistItemWorklistItem338 WorklistItem(ActionType Action, const FrameIndexEntry &FIE, 339 unsigned AffectedReg) 340 : Action(Action), FIEToInsert(FIE), AffectedReg(AffectedReg) {} 341 }; 342 343 /// Insertion todo items scheduled to happen at the end of BBs. Since we 344 /// can't annotate BBs we maintain this bookkeeping here. 345 DenseMap<BinaryBasicBlock *, std::vector<WorklistItem>> Todo; 346 347 /// Annotation name used to tag instructions with removal or insertion actions getAnnotationName()348 static StringRef getAnnotationName() { return StringRef("ShrinkWrap-Todo"); } 349 getAnnotationIndex()350 unsigned getAnnotationIndex() { 351 if (AnnotationIndex) 352 return *AnnotationIndex; 353 AnnotationIndex = BC.MIB->getOrCreateAnnotationIndex(getAnnotationName()); 354 return *AnnotationIndex; 355 } 356 357 private: 358 using BBIterTy = BinaryBasicBlock::iterator; 359 360 /// Calculate all possible uses/defs of these callee-saved regs 361 void classifyCSRUses(); 362 363 // Ensure we don't work on cases where there are no uses of the callee-saved 364 // register. These unnecessary spills should have been removed by previous 365 // passes. 366 void pruneUnwantedCSRs(); 367 368 // Map regs to their possible save possibilities (at start of these BBs) 369 void computeSaveLocations(); 370 371 /// Look into the best save location found for saving callee-saved reg 372 /// \p CSR and evaluates whether we would benefit by moving the spill to this 373 /// new save location. Returns true in case it is profitable to perform the 374 /// move. 375 bool validateBestSavePos(unsigned CSR, MCInst *&BestPosSave, 376 uint64_t &TotalEstimatedWin); 377 378 /// Populate the Todo map with worklistitems to change the function scheduleChange(ProgramPoint PP,T &&...Item)379 template <typename... T> void scheduleChange(ProgramPoint PP, T &&...Item) { 380 if (PP.isInst()) { 381 auto &WList = BC.MIB->getOrCreateAnnotationAs<std::vector<WorklistItem>>( 382 *PP.getInst(), getAnnotationIndex(), AllocatorId); 383 WList.emplace_back(std::forward<T>(Item)...); 384 return; 385 } 386 BinaryBasicBlock *BB = PP.getBB(); 387 // Avoid inserting on BBs with no instructions because we have a dataflow 388 // analysis that depends on insertions happening before real instructions 389 // (PredictiveStackPointerTracking) 390 assert(BB->size() != 0 && 391 "doRestorePlacement() should have handled empty BBs"); 392 Todo[BB].emplace_back(std::forward<T>(Item)...); 393 } 394 395 /// Determine the POP ordering according to which CSR save is the dominator. 396 void computeDomOrder(); 397 398 /// Check that the best possible location for a spill save (as determined by 399 /// computeSaveLocations) is cold enough to be worth moving the save to it. 400 /// \p CSR is the callee-saved register number, \p BestPosSave returns the 401 /// pointer to the cold location in case the function returns true, while 402 /// \p TotalEstimatedWin contains the ins dyn count reduction after moving. 403 bool isBestSavePosCold(unsigned CSR, MCInst *&BestPosSave, 404 uint64_t &TotalEstimatedWin); 405 406 /// Auxiliary function used to create basic blocks for critical edges and 407 /// update the dominance frontier with these new locations 408 void splitFrontierCritEdges( 409 BinaryFunction *Func, SmallVector<ProgramPoint, 4> &Frontier, 410 const SmallVector<bool, 4> &IsCritEdge, 411 const SmallVector<BinaryBasicBlock *, 4> &From, 412 const SmallVector<SmallVector<BinaryBasicBlock *, 4>, 4> &To); 413 414 /// After the best save location for a spill has been established in 415 /// \p BestPosSave for reg \p CSR, compute adequate locations to restore 416 /// the spilled value. This will be at the dominance frontier. 417 /// Returns an empty vector if we failed. In case of success, set 418 /// \p UsePushPops to true if we can operate in the push/pops mode. 419 SmallVector<ProgramPoint, 4> doRestorePlacement(MCInst *BestPosSave, 420 unsigned CSR, 421 uint64_t TotalEstimatedWin); 422 423 /// Checks whether using push and pops (instead of the longer load-store 424 /// counterparts) is correct for reg \p CSR 425 bool validatePushPopsMode(unsigned CSR, MCInst *BestPosSave, 426 int64_t SaveOffset); 427 428 /// Adjust restore locations to the correct SP offset if we are using POPs 429 /// instead of random-access load instructions. 430 SmallVector<ProgramPoint, 4> 431 fixPopsPlacements(const SmallVector<ProgramPoint, 4> &RestorePoints, 432 int64_t SaveOffset, unsigned CSR); 433 434 /// When moving spills, mark all old spill locations to be deleted 435 void scheduleOldSaveRestoresRemoval(unsigned CSR, bool UsePushPops); 436 /// Return true if \p Inst uses reg \p CSR 437 bool doesInstUsesCSR(const MCInst &Inst, uint16_t CSR); 438 /// When moving spills, mark all new spill locations for insertion 439 void 440 scheduleSaveRestoreInsertions(unsigned CSR, MCInst *BestPosSave, 441 SmallVector<ProgramPoint, 4> &RestorePoints, 442 bool UsePushPops); 443 444 /// Coordinate the replacement of callee-saved spills from their original 445 /// place (at prologue and epilogues) to colder basic blocks as determined 446 /// by computeSaveLocations(). 447 void moveSaveRestores(); 448 449 /// Compare multiple basic blocks created by splitting critical edges. If they 450 /// have the same contents and successor, fold them into one. 451 bool foldIdenticalSplitEdges(); 452 453 /// After the spill locations for reg \p CSR has been moved and all affected 454 /// CFI has been removed, insert new updated CFI information for these 455 /// locations. 456 void insertUpdatedCFI(unsigned CSR, int SPValPush, int SPValPop); 457 458 /// In case the function anchors the CFA reg as SP and we inserted pushes/pops 459 /// insert def_cfa_offsets at appropriate places (and delete old 460 /// def_cfa_offsets) 461 void rebuildCFIForSP(); 462 463 /// Rebuild all CFI for affected Callee-Saved Registers. 464 void rebuildCFI(); 465 466 /// Create a load-store instruction (depending on the contents of \p FIE). 467 /// If \p CreatePushOrPop is true, create a push/pop instead. Current SP/FP 468 /// values, as determined by StackPointerTracking, should be informed via 469 /// \p SPVal and \p FPVal in order to emit the correct offset form SP/FP. 470 Expected<MCInst> createStackAccess(int SPVal, int FPVal, 471 const FrameIndexEntry &FIE, 472 bool CreatePushOrPop); 473 474 /// Update the CFI referenced by \p Inst with \p NewOffset, if the CFI has 475 /// an offset. 476 void updateCFIInstOffset(MCInst &Inst, int64_t NewOffset); 477 478 /// Insert any CFI that should be attached to a register spill save/restore. 479 BBIterTy insertCFIsForPushOrPop(BinaryBasicBlock &BB, BBIterTy Pos, 480 unsigned Reg, bool IsPush, int Sz, 481 int64_t NewOffset); 482 483 /// Auxiliary function to processInsertionsList, adding a new instruction 484 /// before \p InsertionPoint as requested by \p Item. Return an updated 485 /// InsertionPoint for other instructions that need to be inserted at the same 486 /// original location, since this insertion may have invalidated the previous 487 /// location. 488 Expected<BBIterTy> processInsertion(BBIterTy InsertionPoint, 489 BinaryBasicBlock *CurBB, 490 const WorklistItem &Item, int64_t SPVal, 491 int64_t FPVal); 492 493 /// Auxiliary function to processInsertions(), helping perform all the 494 /// insertion tasks in the todo list associated with a single insertion point. 495 /// Return true if at least one insertion was performed. 496 Expected<BBIterTy> processInsertionsList(BBIterTy InsertionPoint, 497 BinaryBasicBlock *CurBB, 498 std::vector<WorklistItem> &TodoList, 499 int64_t SPVal, int64_t FPVal); 500 501 /// Apply all insertion todo tasks regarding insertion of new stores/loads or 502 /// push/pops at annotated points. Return false if the entire function had 503 /// no todo tasks annotation and this pass has nothing to do. 504 Expected<bool> processInsertions(); 505 506 /// Apply all deletion todo tasks (or tasks to change a push/pop to a memory 507 /// access no-op) 508 void processDeletions(); 509 510 public: ShrinkWrapping(const FrameAnalysis & FA,BinaryFunction & BF,DataflowInfoManager & Info,MCPlusBuilder::AllocatorIdTy AllocId)511 ShrinkWrapping(const FrameAnalysis &FA, BinaryFunction &BF, 512 DataflowInfoManager &Info, 513 MCPlusBuilder::AllocatorIdTy AllocId) 514 : FA(FA), BC(BF.getBinaryContext()), BF(BF), Info(Info), 515 AllocatorId(AllocId), SLM(FA, BF, Info, AllocId), 516 CSA(FA, BF, Info, AllocId) {} 517 ~ShrinkWrapping()518 ~ShrinkWrapping() { 519 for (BinaryBasicBlock &BB : BF) 520 for (MCInst &Inst : BB) 521 BC.MIB->removeAnnotation(Inst, getAnnotationIndex()); 522 } 523 524 Expected<bool> perform(bool HotOnly = false); 525 526 static void printStats(BinaryContext &BC); 527 }; 528 529 } // end namespace bolt 530 } // end namespace llvm 531 532 #endif 533