Make core BOLT functionality more friendly to being used as a library instead of in our standalone driver llvm-bolt. To accomplish this, we augment BinaryContext with journaling streams that are to be used by most BOLT code whenever something needs to be logged to the screen. Users of the library can decide if logs should be printed to a file, no file or to the screen, as before. To illustrate this, this patch adds a new option `--log-file` that allows the user to redirect BOLT logging to a file on disk or completely hide it by using `--log-file=/dev/null`. Future BOLT code should now use `BinaryContext::outs()` for printing important messages instead of `llvm::outs()`. A new test log.test enforces this by verifying that no strings are print to screen once the `--log-file` option is used. In previous patches we also added a new BOLTError class to report common and fatal errors, so code shouldn't call exit(1) now. To easily handle problems as before (by quitting with exit(1)), callers can now use `BinaryContext::logBOLTErrorsAndQuitOnFatal(Error)` whenever code needs to deal with BOLT errors. To test this, we have fatal.s that checks we are correctly quitting and printing a fatal error to the screen. Because this is a significant change by itself, not all code was yet ported. Code from Profiler libs (DataAggregator and friends) still print errors directly to screen. Co-authored-by: Rafael Auler <rafaelauler@fb.com> Test Plan: NFC
641 lines
21 KiB
C++
641 lines
21 KiB
C++
//===- 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/ParallelUtilities.h"
|
|
#include "bolt/Passes/CallGraphWalker.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();
|
|
|
|
// Clean up memory allocated for annotation values
|
|
if (!opts::NoThreads)
|
|
for (MCPlusBuilder::AllocatorIdTy Id : SPTAllocatorsId)
|
|
BC.MIB->freeValuesAllocator(Id);
|
|
}
|
|
}
|
|
|
|
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
|