| //===- bolt/Passes/FrameAnalysis.cpp --------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file implements the FrameAnalysis class. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "bolt/Passes/FrameAnalysis.h" |
| #include "bolt/Core/CallGraphWalker.h" |
| #include "bolt/Core/ParallelUtilities.h" |
| #include "llvm/MC/MCRegisterInfo.h" |
| #include "llvm/Support/Timer.h" |
| #include <fstream> |
| #include <stack> |
| |
| #define DEBUG_TYPE "fa" |
| |
| using namespace llvm; |
| |
| namespace opts { |
| extern cl::OptionCategory BoltOptCategory; |
| extern cl::opt<unsigned> Verbosity; |
| |
| static cl::list<std::string> |
| FrameOptFunctionNames("funcs-fop", cl::CommaSeparated, |
| cl::desc("list of functions to apply frame opts"), |
| cl::value_desc("func1,func2,func3,...")); |
| |
| static cl::opt<std::string> FrameOptFunctionNamesFile( |
| "funcs-file-fop", |
| cl::desc("file with list of functions to frame optimize")); |
| |
| static cl::opt<bool> TimeFA("time-fa", cl::desc("time frame analysis steps"), |
| cl::ReallyHidden, cl::cat(BoltOptCategory)); |
| |
| static cl::opt<bool> |
| ExperimentalSW("experimental-shrink-wrapping", |
| cl::desc("process functions with stack pointer arithmetic"), |
| cl::ReallyHidden, cl::ZeroOrMore, cl::cat(BoltOptCategory)); |
| |
| bool shouldFrameOptimize(const llvm::bolt::BinaryFunction &Function) { |
| if (Function.hasUnknownControlFlow()) |
| return false; |
| |
| if (!FrameOptFunctionNamesFile.empty()) { |
| assert(!FrameOptFunctionNamesFile.empty() && "unexpected empty file name"); |
| std::ifstream FuncsFile(FrameOptFunctionNamesFile, std::ios::in); |
| std::string FuncName; |
| while (std::getline(FuncsFile, FuncName)) |
| FrameOptFunctionNames.push_back(FuncName); |
| FrameOptFunctionNamesFile = ""; |
| } |
| |
| if (FrameOptFunctionNames.empty()) |
| return true; |
| return llvm::any_of(FrameOptFunctionNames, [&](std::string &Name) { |
| return Function.hasName(Name); |
| }); |
| } |
| } // namespace opts |
| |
| namespace llvm { |
| namespace bolt { |
| |
| raw_ostream &operator<<(raw_ostream &OS, const FrameIndexEntry &FIE) { |
| OS << "FrameIndexEntry<IsLoad: " << FIE.IsLoad << ", IsStore: " << FIE.IsStore |
| << ", IsStoreFromReg: " << FIE.IsStoreFromReg |
| << ", RegOrImm: " << FIE.RegOrImm << ", StackOffset: "; |
| if (FIE.StackOffset < 0) |
| OS << "-" << Twine::utohexstr(-FIE.StackOffset); |
| else |
| OS << "+" << Twine::utohexstr(FIE.StackOffset); |
| OS << ", Size: " << static_cast<int>(FIE.Size) |
| << ", IsSimple: " << FIE.IsSimple << ">"; |
| return OS; |
| } |
| |
| namespace { |
| |
| /// This class should be used to iterate through basic blocks in layout order |
| /// to analyze instructions for frame accesses. The user should call |
| /// enterNewBB() whenever starting analyzing a new BB and doNext() for each |
| /// instruction. After doNext(), if isValidAccess() returns true, it means the |
| /// current instruction accesses the frame and getFIE() may be used to obtain |
| /// details about this access. |
| class FrameAccessAnalysis { |
| /// We depend on Stack Pointer Tracking to figure out the current SP offset |
| /// value at a given program point |
| StackPointerTracking &SPT; |
| |
| /// Context vars |
| const BinaryContext &BC; |
| const BinaryFunction &BF; |
| // Vars used for storing useful CFI info to give us a hint about how the stack |
| // is used in this function |
| int SPOffset{0}; |
| int FPOffset{0}; |
| int64_t CfaOffset{-8}; |
| uint16_t CfaReg{7}; |
| std::stack<std::pair<int64_t, uint16_t>> CFIStack; |
| /// Our pointer to access SPT info |
| const MCInst *Prev{nullptr}; |
| /// Info about the last frame access |
| bool IsValidAccess{false}; |
| bool EscapesStackAddress{false}; |
| FrameIndexEntry FIE; |
| |
| bool decodeFrameAccess(const MCInst &Inst) { |
| int32_t SrcImm = 0; |
| MCPhysReg Reg = 0; |
| int64_t StackOffset = 0; |
| bool IsIndexed = false; |
| if (!BC.MIB->isStackAccess( |
| Inst, FIE.IsLoad, FIE.IsStore, FIE.IsStoreFromReg, Reg, SrcImm, |
| FIE.StackPtrReg, StackOffset, FIE.Size, FIE.IsSimple, IsIndexed)) { |
| return true; |
| } |
| |
| if (IsIndexed || (!FIE.Size && (FIE.IsLoad || FIE.IsStore))) { |
| LLVM_DEBUG(dbgs() << "Giving up on indexed memory access/unknown size\n"); |
| LLVM_DEBUG(dbgs() << "Blame insn: "); |
| LLVM_DEBUG(BC.printInstruction(dbgs(), Inst, 0, &BF, true, false, false)); |
| LLVM_DEBUG(Inst.dump()); |
| return false; |
| } |
| |
| assert(FIE.Size != 0 || (!FIE.IsLoad && !FIE.IsStore)); |
| |
| FIE.RegOrImm = SrcImm; |
| if (FIE.IsLoad || FIE.IsStoreFromReg) |
| FIE.RegOrImm = Reg; |
| |
| if (FIE.StackPtrReg == BC.MIB->getStackPointer() && SPOffset != SPT.EMPTY && |
| SPOffset != SPT.SUPERPOSITION) { |
| LLVM_DEBUG( |
| dbgs() << "Adding access via SP while CFA reg is another one\n"); |
| FIE.StackOffset = SPOffset + StackOffset; |
| } else if (FIE.StackPtrReg == BC.MIB->getFramePointer() && |
| FPOffset != SPT.EMPTY && FPOffset != SPT.SUPERPOSITION) { |
| LLVM_DEBUG( |
| dbgs() << "Adding access via FP while CFA reg is another one\n"); |
| FIE.StackOffset = FPOffset + StackOffset; |
| } else if (FIE.StackPtrReg == |
| *BC.MRI->getLLVMRegNum(CfaReg, /*isEH=*/false)) { |
| FIE.StackOffset = CfaOffset + StackOffset; |
| } else { |
| LLVM_DEBUG( |
| dbgs() << "Found stack access with reg different than cfa reg.\n"); |
| LLVM_DEBUG(dbgs() << "\tCurrent CFA reg: " << CfaReg |
| << "\n\tStack access reg: " << FIE.StackPtrReg << "\n"); |
| LLVM_DEBUG(dbgs() << "Blame insn: "); |
| LLVM_DEBUG(Inst.dump()); |
| return false; |
| } |
| IsValidAccess = true; |
| return true; |
| } |
| |
| public: |
| FrameAccessAnalysis(BinaryFunction &BF, StackPointerTracking &SPT) |
| : SPT(SPT), BC(BF.getBinaryContext()), BF(BF) {} |
| |
| void enterNewBB() { Prev = nullptr; } |
| const FrameIndexEntry &getFIE() const { return FIE; } |
| int getSPOffset() const { return SPOffset; } |
| bool isValidAccess() const { return IsValidAccess; } |
| bool doesEscapeStackAddress() const { return EscapesStackAddress; } |
| |
| bool doNext(const BinaryBasicBlock &BB, const MCInst &Inst) { |
| IsValidAccess = false; |
| EscapesStackAddress = false; |
| std::tie(SPOffset, FPOffset) = |
| Prev ? *SPT.getStateAt(*Prev) : *SPT.getStateAt(BB); |
| Prev = &Inst; |
| // Use CFI information to keep track of which register is being used to |
| // access the frame |
| if (BC.MIB->isCFI(Inst)) { |
| const MCCFIInstruction *CFI = BF.getCFIFor(Inst); |
| switch (CFI->getOperation()) { |
| case MCCFIInstruction::OpDefCfa: |
| CfaOffset = CFI->getOffset(); |
| [[fallthrough]]; |
| case MCCFIInstruction::OpDefCfaRegister: |
| CfaReg = CFI->getRegister(); |
| break; |
| case MCCFIInstruction::OpDefCfaOffset: |
| CfaOffset = CFI->getOffset(); |
| break; |
| case MCCFIInstruction::OpRememberState: |
| CFIStack.push(std::make_pair(CfaOffset, CfaReg)); |
| break; |
| case MCCFIInstruction::OpRestoreState: { |
| if (CFIStack.empty()) |
| dbgs() << "Assertion is about to fail: " << BF.getPrintName() << "\n"; |
| assert(!CFIStack.empty() && "Corrupt CFI stack"); |
| std::pair<int64_t, uint16_t> &Elem = CFIStack.top(); |
| CFIStack.pop(); |
| CfaOffset = Elem.first; |
| CfaReg = Elem.second; |
| break; |
| } |
| case MCCFIInstruction::OpAdjustCfaOffset: |
| llvm_unreachable("Unhandled AdjustCfaOffset"); |
| break; |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| if (BC.MIB->escapesVariable(Inst, SPT.HasFramePointer)) { |
| EscapesStackAddress = true; |
| if (!opts::ExperimentalSW) { |
| LLVM_DEBUG( |
| dbgs() << "Leaked stack address, giving up on this function.\n"); |
| LLVM_DEBUG(dbgs() << "Blame insn: "); |
| LLVM_DEBUG(Inst.dump()); |
| return false; |
| } |
| } |
| |
| return decodeFrameAccess(Inst); |
| } |
| }; |
| |
| } // end anonymous namespace |
| |
| void FrameAnalysis::addArgAccessesFor(MCInst &Inst, ArgAccesses &&AA) { |
| if (ErrorOr<ArgAccesses &> OldAA = getArgAccessesFor(Inst)) { |
| if (OldAA->AssumeEverything) |
| return; |
| *OldAA = std::move(AA); |
| return; |
| } |
| if (AA.AssumeEverything) { |
| // Index 0 in ArgAccessesVector represents an "assumeeverything" entry |
| BC.MIB->addAnnotation(Inst, "ArgAccessEntry", 0U); |
| return; |
| } |
| BC.MIB->addAnnotation(Inst, "ArgAccessEntry", |
| (unsigned)ArgAccessesVector.size()); |
| ArgAccessesVector.emplace_back(std::move(AA)); |
| } |
| |
| void FrameAnalysis::addArgInStackAccessFor(MCInst &Inst, |
| const ArgInStackAccess &Arg) { |
| ErrorOr<ArgAccesses &> AA = getArgAccessesFor(Inst); |
| if (!AA) { |
| addArgAccessesFor(Inst, ArgAccesses(false)); |
| AA = getArgAccessesFor(Inst); |
| assert(AA && "Object setup failed"); |
| } |
| std::set<ArgInStackAccess> &Set = AA->Set; |
| assert(!AA->AssumeEverything && "Adding arg to AssumeEverything set"); |
| Set.emplace(Arg); |
| } |
| |
| void FrameAnalysis::addFIEFor(MCInst &Inst, const FrameIndexEntry &FIE) { |
| BC.MIB->addAnnotation(Inst, "FrameAccessEntry", (unsigned)FIEVector.size()); |
| FIEVector.emplace_back(FIE); |
| } |
| |
| ErrorOr<ArgAccesses &> FrameAnalysis::getArgAccessesFor(const MCInst &Inst) { |
| if (auto Idx = BC.MIB->tryGetAnnotationAs<unsigned>(Inst, "ArgAccessEntry")) { |
| assert(ArgAccessesVector.size() > *Idx && "Out of bounds"); |
| return ArgAccessesVector[*Idx]; |
| } |
| return make_error_code(errc::result_out_of_range); |
| } |
| |
| ErrorOr<const ArgAccesses &> |
| FrameAnalysis::getArgAccessesFor(const MCInst &Inst) const { |
| if (auto Idx = BC.MIB->tryGetAnnotationAs<unsigned>(Inst, "ArgAccessEntry")) { |
| assert(ArgAccessesVector.size() > *Idx && "Out of bounds"); |
| return ArgAccessesVector[*Idx]; |
| } |
| return make_error_code(errc::result_out_of_range); |
| } |
| |
| ErrorOr<const FrameIndexEntry &> |
| FrameAnalysis::getFIEFor(const MCInst &Inst) const { |
| if (auto Idx = |
| BC.MIB->tryGetAnnotationAs<unsigned>(Inst, "FrameAccessEntry")) { |
| assert(FIEVector.size() > *Idx && "Out of bounds"); |
| return FIEVector[*Idx]; |
| } |
| return make_error_code(errc::result_out_of_range); |
| } |
| |
| void FrameAnalysis::traverseCG(BinaryFunctionCallGraph &CG) { |
| CallGraphWalker CGWalker(CG); |
| |
| CGWalker.registerVisitor( |
| [&](BinaryFunction *Func) -> bool { return computeArgsAccessed(*Func); }); |
| |
| CGWalker.walk(); |
| |
| DEBUG_WITH_TYPE("ra", { |
| for (auto &MapEntry : ArgsTouchedMap) { |
| const BinaryFunction *Func = MapEntry.first; |
| const auto &Set = MapEntry.second; |
| dbgs() << "Args accessed for " << Func->getPrintName() << ": "; |
| if (!Set.empty() && Set.count(std::make_pair(-1, 0))) |
| dbgs() << "assume everything"; |
| else |
| for (const std::pair<int64_t, uint8_t> &Entry : Set) |
| dbgs() << "[" << Entry.first << ", " << (int)Entry.second << "] "; |
| dbgs() << "\n"; |
| } |
| }); |
| } |
| |
| bool FrameAnalysis::updateArgsTouchedFor(const BinaryFunction &BF, MCInst &Inst, |
| int CurOffset) { |
| if (!BC.MIB->isCall(Inst)) |
| return false; |
| |
| std::set<int64_t> Res; |
| const MCSymbol *TargetSymbol = BC.MIB->getTargetSymbol(Inst); |
| // If indirect call, we conservatively assume it accesses all stack positions |
| if (TargetSymbol == nullptr) { |
| addArgAccessesFor(Inst, ArgAccesses(/*AssumeEverything=*/true)); |
| if (!FunctionsRequireAlignment.count(&BF)) { |
| FunctionsRequireAlignment.insert(&BF); |
| return true; |
| } |
| return false; |
| } |
| |
| const BinaryFunction *Function = BC.getFunctionForSymbol(TargetSymbol); |
| // Call to a function without a BinaryFunction object. Conservatively assume |
| // it accesses all stack positions |
| if (Function == nullptr) { |
| addArgAccessesFor(Inst, ArgAccesses(/*AssumeEverything=*/true)); |
| if (!FunctionsRequireAlignment.count(&BF)) { |
| FunctionsRequireAlignment.insert(&BF); |
| return true; |
| } |
| return false; |
| } |
| |
| auto Iter = ArgsTouchedMap.find(Function); |
| |
| bool Changed = false; |
| if (BC.MIB->isTailCall(Inst) && Iter != ArgsTouchedMap.end()) { |
| // Ignore checking CurOffset because we can't always reliably determine the |
| // offset specially after an epilogue, where tailcalls happen. It should be |
| // -8. |
| for (std::pair<int64_t, uint8_t> Elem : Iter->second) { |
| if (!llvm::is_contained(ArgsTouchedMap[&BF], Elem)) { |
| ArgsTouchedMap[&BF].emplace(Elem); |
| Changed = true; |
| } |
| } |
| } |
| if (FunctionsRequireAlignment.count(Function) && |
| !FunctionsRequireAlignment.count(&BF)) { |
| Changed = true; |
| FunctionsRequireAlignment.insert(&BF); |
| } |
| if (Iter == ArgsTouchedMap.end()) |
| return Changed; |
| |
| if (CurOffset == StackPointerTracking::EMPTY || |
| CurOffset == StackPointerTracking::SUPERPOSITION) { |
| addArgAccessesFor(Inst, ArgAccesses(/*AssumeEverything=*/true)); |
| return Changed; |
| } |
| |
| for (std::pair<int64_t, uint8_t> Elem : Iter->second) { |
| if (Elem.first == -1) { |
| addArgAccessesFor(Inst, ArgAccesses(/*AssumeEverything=*/true)); |
| break; |
| } |
| LLVM_DEBUG(dbgs() << "Added arg in stack access annotation " |
| << CurOffset + Elem.first << "\n"); |
| addArgInStackAccessFor( |
| Inst, ArgInStackAccess{/*StackOffset=*/CurOffset + Elem.first, |
| /*Size=*/Elem.second}); |
| } |
| return Changed; |
| } |
| |
| bool FrameAnalysis::computeArgsAccessed(BinaryFunction &BF) { |
| if (!BF.isSimple() || !BF.hasCFG()) { |
| LLVM_DEBUG(dbgs() << "Treating " << BF.getPrintName() |
| << " conservatively.\n"); |
| ArgsTouchedMap[&BF].emplace(std::make_pair(-1, 0)); |
| if (!FunctionsRequireAlignment.count(&BF)) { |
| FunctionsRequireAlignment.insert(&BF); |
| return true; |
| } |
| return false; |
| } |
| |
| LLVM_DEBUG(dbgs() << "Now computing args accessed for: " << BF.getPrintName() |
| << "\n"); |
| bool UpdatedArgsTouched = false; |
| bool NoInfo = false; |
| FrameAccessAnalysis FAA(BF, getSPT(BF)); |
| |
| for (BinaryBasicBlock *BB : BF.getLayout().blocks()) { |
| FAA.enterNewBB(); |
| |
| for (MCInst &Inst : *BB) { |
| if (!FAA.doNext(*BB, Inst) || FAA.doesEscapeStackAddress()) { |
| ArgsTouchedMap[&BF].emplace(std::make_pair(-1, 0)); |
| NoInfo = true; |
| break; |
| } |
| |
| // Check for calls -- attach stack accessing info to them regarding their |
| // target |
| if (updateArgsTouchedFor(BF, Inst, FAA.getSPOffset())) |
| UpdatedArgsTouched = true; |
| |
| // Check for stack accesses that affect callers |
| if (!FAA.isValidAccess()) |
| continue; |
| |
| const FrameIndexEntry &FIE = FAA.getFIE(); |
| if (FIE.StackOffset < 0) |
| continue; |
| if (ArgsTouchedMap[&BF].find(std::make_pair(FIE.StackOffset, FIE.Size)) != |
| ArgsTouchedMap[&BF].end()) |
| continue; |
| |
| // Record accesses to the previous stack frame |
| ArgsTouchedMap[&BF].emplace(std::make_pair(FIE.StackOffset, FIE.Size)); |
| UpdatedArgsTouched = true; |
| LLVM_DEBUG({ |
| dbgs() << "Arg access offset " << FIE.StackOffset << " added to:\n"; |
| BC.printInstruction(dbgs(), Inst, 0, &BF, true); |
| }); |
| } |
| if (NoInfo) |
| break; |
| } |
| if (FunctionsRequireAlignment.count(&BF)) |
| return UpdatedArgsTouched; |
| |
| if (NoInfo) { |
| FunctionsRequireAlignment.insert(&BF); |
| return true; |
| } |
| |
| for (BinaryBasicBlock &BB : BF) { |
| for (MCInst &Inst : BB) { |
| if (BC.MIB->requiresAlignedAddress(Inst)) { |
| FunctionsRequireAlignment.insert(&BF); |
| return true; |
| } |
| } |
| } |
| return UpdatedArgsTouched; |
| } |
| |
| bool FrameAnalysis::restoreFrameIndex(BinaryFunction &BF) { |
| FrameAccessAnalysis FAA(BF, getSPT(BF)); |
| |
| LLVM_DEBUG(dbgs() << "Restoring frame indices for \"" << BF.getPrintName() |
| << "\"\n"); |
| for (BinaryBasicBlock *BB : BF.getLayout().blocks()) { |
| LLVM_DEBUG(dbgs() << "\tNow at BB " << BB->getName() << "\n"); |
| FAA.enterNewBB(); |
| |
| for (MCInst &Inst : *BB) { |
| if (!FAA.doNext(*BB, Inst)) |
| return false; |
| LLVM_DEBUG({ |
| dbgs() << "\t\tNow at "; |
| Inst.dump(); |
| dbgs() << "\t\t\tSP offset is " << FAA.getSPOffset() << "\n"; |
| }); |
| |
| if (FAA.doesEscapeStackAddress()) { |
| if (!FunctionsWithStackArithmetic.count(&BF)) |
| FunctionsWithStackArithmetic.insert(&BF); |
| continue; |
| } |
| |
| if (!FAA.isValidAccess()) |
| continue; |
| |
| const FrameIndexEntry &FIE = FAA.getFIE(); |
| |
| addFIEFor(Inst, FIE); |
| LLVM_DEBUG({ |
| dbgs() << "Frame index annotation " << FIE << " added to:\n"; |
| BC.printInstruction(dbgs(), Inst, 0, &BF, true); |
| }); |
| } |
| } |
| return true; |
| } |
| |
| void FrameAnalysis::cleanAnnotations() { |
| NamedRegionTimer T("cleanannotations", "clean annotations", "FA", |
| "FA breakdown", opts::TimeFA); |
| |
| ParallelUtilities::WorkFuncTy CleanFunction = [&](BinaryFunction &BF) { |
| for (BinaryBasicBlock &BB : BF) { |
| for (MCInst &Inst : BB) { |
| BC.MIB->removeAnnotation(Inst, "ArgAccessEntry"); |
| BC.MIB->removeAnnotation(Inst, "FrameAccessEntry"); |
| } |
| } |
| }; |
| |
| ParallelUtilities::runOnEachFunction( |
| BC, ParallelUtilities::SchedulingPolicy::SP_INST_LINEAR, CleanFunction, |
| ParallelUtilities::PredicateTy(nullptr), "cleanAnnotations"); |
| } |
| |
| FrameAnalysis::FrameAnalysis(BinaryContext &BC, BinaryFunctionCallGraph &CG) |
| : BC(BC) { |
| // Position 0 of the vector should be always associated with "assume access |
| // everything". |
| ArgAccessesVector.emplace_back(ArgAccesses(/*AssumeEverything*/ true)); |
| |
| if (!opts::NoThreads) { |
| NamedRegionTimer T1("precomputespt", "pre-compute spt", "FA", |
| "FA breakdown", opts::TimeFA); |
| preComputeSPT(); |
| } |
| |
| { |
| NamedRegionTimer T1("traversecg", "traverse call graph", "FA", |
| "FA breakdown", opts::TimeFA); |
| traverseCG(CG); |
| } |
| |
| for (auto &I : BC.getBinaryFunctions()) { |
| CountDenominator += I.second.getFunctionScore(); |
| |
| // "shouldOptimize" for passes that run after finalize |
| if (!(I.second.isSimple() && I.second.hasCFG() && !I.second.isIgnored()) || |
| !opts::shouldFrameOptimize(I.second)) { |
| ++NumFunctionsNotOptimized; |
| continue; |
| } |
| |
| { |
| NamedRegionTimer T1("restorefi", "restore frame index", "FA", |
| "FA breakdown", opts::TimeFA); |
| if (!restoreFrameIndex(I.second)) { |
| ++NumFunctionsFailedRestoreFI; |
| CountFunctionsFailedRestoreFI += I.second.getFunctionScore(); |
| continue; |
| } |
| } |
| AnalyzedFunctions.insert(&I.second); |
| } |
| |
| { |
| NamedRegionTimer T1("clearspt", "clear spt", "FA", "FA breakdown", |
| opts::TimeFA); |
| clearSPTMap(); |
| } |
| } |
| |
| void FrameAnalysis::printStats() { |
| BC.outs() << "BOLT-INFO: FRAME ANALYSIS: " << NumFunctionsNotOptimized |
| << " function(s) were not optimized.\n" |
| << "BOLT-INFO: FRAME ANALYSIS: " << NumFunctionsFailedRestoreFI |
| << " function(s) " |
| << format( |
| "(%.1lf%% dyn cov)", |
| (100.0 * CountFunctionsFailedRestoreFI / CountDenominator)) |
| << " could not have its frame indices restored.\n"; |
| } |
| |
| void FrameAnalysis::clearSPTMap() { |
| if (opts::NoThreads) { |
| SPTMap.clear(); |
| return; |
| } |
| |
| ParallelUtilities::WorkFuncTy ClearFunctionSPT = [&](BinaryFunction &BF) { |
| std::unique_ptr<StackPointerTracking> &SPTPtr = SPTMap.find(&BF)->second; |
| SPTPtr.reset(); |
| }; |
| |
| ParallelUtilities::PredicateTy SkipFunc = [&](const BinaryFunction &BF) { |
| return !BF.isSimple() || !BF.hasCFG(); |
| }; |
| |
| ParallelUtilities::runOnEachFunction( |
| BC, ParallelUtilities::SchedulingPolicy::SP_INST_LINEAR, ClearFunctionSPT, |
| SkipFunc, "clearSPTMap"); |
| |
| SPTMap.clear(); |
| } |
| |
| void FrameAnalysis::preComputeSPT() { |
| // Make sure that the SPTMap is empty |
| assert(SPTMap.size() == 0); |
| |
| // Create map entries to allow lock-free parallel execution |
| for (auto &BFI : BC.getBinaryFunctions()) { |
| BinaryFunction &BF = BFI.second; |
| if (!BF.isSimple() || !BF.hasCFG()) |
| continue; |
| SPTMap.emplace(&BF, std::unique_ptr<StackPointerTracking>()); |
| } |
| |
| // Create an index for the SPT annotation to allow lock-free parallel |
| // execution |
| BC.MIB->getOrCreateAnnotationIndex("StackPointerTracking"); |
| |
| // Run SPT in parallel |
| ParallelUtilities::WorkFuncWithAllocTy ProcessFunction = |
| [&](BinaryFunction &BF, MCPlusBuilder::AllocatorIdTy AllocId) { |
| std::unique_ptr<StackPointerTracking> &SPTPtr = |
| SPTMap.find(&BF)->second; |
| SPTPtr = std::make_unique<StackPointerTracking>(BF, AllocId); |
| SPTPtr->run(); |
| }; |
| |
| ParallelUtilities::PredicateTy SkipPredicate = [&](const BinaryFunction &BF) { |
| return !BF.isSimple() || !BF.hasCFG(); |
| }; |
| |
| ParallelUtilities::runOnEachFunctionWithUniqueAllocId( |
| BC, ParallelUtilities::SchedulingPolicy::SP_BB_QUADRATIC, ProcessFunction, |
| SkipPredicate, "preComputeSPT"); |
| } |
| |
| } // namespace bolt |
| } // namespace llvm |