[BOLT] Add dataflow infrastructure

Summary:
This diff introduces a common infrastructure for performing
dataflow analyses in BinaryFunctions as well as a few analyses that are
useful in a variety of scenarios. The largest user of this
infrastructure so far is shrink wrapping, which will be added in a
separate diff.

(cherry picked from FBD4983671)
This commit is contained in:
Rafael Auler 2017-05-01 16:51:27 -07:00 committed by Maksim Panchenko
parent 457b7f14b9
commit 511a1c78b2
16 changed files with 2499 additions and 4 deletions

View File

@ -974,6 +974,20 @@ public:
return nullptr;
}
/// Retrieve the landing pad BB associated with invoke instruction \p Invoke
/// that is in \p BB. Return nullptr if none exists
BinaryBasicBlock *getLandingPadBBFor(const BinaryBasicBlock &BB,
const MCInst &InvokeInst) {
assert(BC.MIA->isInvoke(InvokeInst) && "must be invoke instruction");
MCLandingPad LP = BC.MIA->getEHInfo(InvokeInst);
if (LP.first) {
auto *LBB = BB.getLandingPad(LP.first);
assert (LBB && "Landing pad should be defined");
return LBB;
}
return nullptr;
}
/// Return the name of the function as extracted from the binary file.
/// If the function has multiple names - return the last one
/// followed by "(*#<numnames>)".
@ -1412,6 +1426,14 @@ public:
return;
}
BinaryBasicBlock::iterator addCFIInstruction(BinaryBasicBlock *BB,
BinaryBasicBlock::iterator Pos,
MCCFIInstruction &&Inst) {
auto Idx = FrameInstructions.size();
FrameInstructions.emplace_back(std::forward<MCCFIInstruction>(Inst));
return addCFIPseudo(BB, Pos, Idx);
}
/// Insert a CFI pseudo instruction in a basic block. This pseudo instruction
/// is a placeholder that refers to a real MCCFIInstruction object kept by
/// this function that will be emitted at that position.
@ -1424,6 +1446,14 @@ public:
}
/// Retrieve the MCCFIInstruction object associated with a CFI pseudo.
MCCFIInstruction* getCFIFor(const MCInst &Instr) {
if (!BC.MIA->isCFI(Instr))
return nullptr;
uint32_t Offset = Instr.getOperand(0).getImm();
assert(Offset < FrameInstructions.size() && "Invalid CFI offset");
return &FrameInstructions[Offset];
}
const MCCFIInstruction* getCFIFor(const MCInst &Instr) const {
if (!BC.MIA->isCFI(Instr))
return nullptr;

View File

@ -1,11 +1,16 @@
add_llvm_library(LLVMBOLTPasses
BinaryPasses.cpp
DataflowAnalysis.cpp
DataflowInfoManager.cpp
FrameAnalysis.cpp
FrameOptimizer.cpp
HFSort.cpp
HFSortPlus.cpp
IndirectCallPromotion.cpp
Inliner.cpp
LivenessAnalysis.cpp
ReorderAlgorithm.cpp
StackPointerTracking.cpp
)
include_directories( ${LLVM_MAIN_SRC_DIR}/tools/llvm-bolt )

View File

@ -0,0 +1,40 @@
#include "DataflowAnalysis.h"
namespace llvm {
namespace bolt {
void doForAllPreds(const BinaryContext &BC, const BinaryBasicBlock &BB,
std::function<void(ProgramPoint)> Task) {
for (auto Pred : BB.predecessors()) {
if (Pred->isValid())
Task(ProgramPoint::getLastPointAt(*Pred));
}
if (!BB.isLandingPad())
return;
for (auto Thrower : BB.throwers()) {
for (auto &Inst : *Thrower) {
if (!BC.MIA->isInvoke(Inst) ||
BC.MIA->getEHInfo(Inst).first != BB.getLabel())
continue;
Task(ProgramPoint(&Inst));
}
}
}
/// Operates on all successors of a basic block.
void doForAllSuccs(const BinaryBasicBlock &BB,
std::function<void(ProgramPoint)> Task) {
for (auto Succ : BB.successors()) {
if (Succ->isValid())
Task(ProgramPoint::getFirstPointAt(*Succ));
}
}
} // namespace bolt
} // namespace llvm
llvm::raw_ostream &llvm::operator<<(llvm::raw_ostream &OS,
const BitVector &Val) {
OS << "BitVector";
return OS;
}

View File

@ -0,0 +1,529 @@
//===--- Passes/DataflowAnalysis.h ----------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_LLVM_BOLT_PASSES_DATAFLOWANALYSIS_H
#define LLVM_TOOLS_LLVM_BOLT_PASSES_DATAFLOWANALYSIS_H
#include "BinaryContext.h"
#include "BinaryFunction.h"
#include "llvm/Support/Timer.h"
#include <queue>
namespace llvm {
namespace bolt {
/// Represents a given program point as viewed by a dataflow analysis. This
/// point is a location that may be either an instruction or a basic block.
/// Example:
///
/// BB1: --> ProgramPoint 1 (stored as bb *)
/// add --> ProgramPoint 2 (stored as inst *)
/// sub --> ProgramPoint 3 (stored as inst *)
/// jmp --> ProgramPoint 4 (stored as inst *)
///
/// ProgramPoints allow us to attach a state to any location in the program
/// and is a core concept used in the dataflow analysis engine.
///
/// A dataflow analysis will associate a state with a program point. In
/// analyses whose direction is forward, this state tracks what happened after
/// the execution of an instruction, and the BB tracks the state of what
/// happened before the execution of the first instruction in this BB. For
/// backwards dataflow analyses, state tracks what happened before the
/// execution of a given instruction, while the state associated with a BB
/// tracks what happened after the execution of the last instruction of a BB.
class ProgramPoint {
enum IDTy : bool { BB = 0, Inst } ID;
union DataU {
BinaryBasicBlock *BB;
MCInst *Inst;
DataU(BinaryBasicBlock *BB) : BB(BB) {}
DataU(MCInst *Inst) : Inst(Inst) {}
} Data;
public:
ProgramPoint() : ID(IDTy::BB), Data((MCInst *)nullptr) {}
ProgramPoint(BinaryBasicBlock *BB) : ID(IDTy::BB), Data(BB) {}
ProgramPoint(MCInst *Inst) : ID(IDTy::Inst), Data(Inst) {}
/// Convenience function to access the last program point of a basic block,
/// which is equal to its last instruction. If it is empty, it is equal to
/// itself.
static ProgramPoint getLastPointAt(BinaryBasicBlock &BB) {
auto Last = BB.rbegin();
if (Last != BB.rend())
return ProgramPoint(&*Last);
return ProgramPoint(&BB);
}
/// Similar to getLastPointAt.
static ProgramPoint getFirstPointAt(BinaryBasicBlock &BB) {
auto First = BB.begin();
if (First != BB.end())
return ProgramPoint(&*First);
return ProgramPoint(&BB);
}
void operator=(const ProgramPoint &PP) {
ID = PP.ID;
Data.BB = PP.Data.BB;
}
bool operator<(const ProgramPoint &PP) const { return Data.BB < PP.Data.BB; }
bool operator==(const ProgramPoint &PP) const {
return Data.BB == PP.Data.BB;
}
bool isBB() const { return ID == IDTy::BB; }
bool isInst() const { return ID == IDTy::Inst; }
BinaryBasicBlock *getBB() const {
assert(isBB());
return Data.BB;
}
MCInst *getInst() const {
assert(isInst());
return Data.Inst;
}
friend DenseMapInfo<ProgramPoint>;
};
/// Convenience function to operate on all predecessors of a BB, as viewed
/// by a dataflow analysis. This includes throw sites if it is a landing pad.
void doForAllPreds(const BinaryContext &BC, const BinaryBasicBlock &BB,
std::function<void(ProgramPoint)> Task);
/// Operates on all successors of a basic block.
void doForAllSuccs(const BinaryBasicBlock &BB,
std::function<void(ProgramPoint)> Task);
/// Base class for dataflow analyses. Depends on the type of whatever object is
/// stored as the state (StateTy) at each program point. The dataflow then
/// updates the state at each program point depending on the instruction being
/// processed, iterating until all points converge and agree on a state value.
/// Remember that depending on how you formulate your dataflow equation, this
/// may not converge and will loop indefinitely.
/// /p Backward indicates the direction of the dataflow. If false, direction is
/// forward.
///
/// Example: Compute the set of live registers at each program point.
///
/// Modelling:
/// Let State be the set of registers that are live. The kill set of a
/// point is the set of all registers clobbered by the instruction at this
/// program point. The gen set is the set of all registers read by it.
///
/// out{b} = Union (s E succs{b}) {in{s}}
/// in{b} = (out{b} - kill{b}) U gen{b}
///
/// Template parameters:
/// StateTy = BitVector, where each index corresponds to a machine register
/// Backward = true (live reg operates in reverse order)
///
/// Subclass implementation notes:
/// Confluence operator = union (if a reg is alive in any succ, it is alive
/// in the current block).
///
template <typename Derived, typename StateTy, bool Backward=false>
class DataflowAnalysis {
/// CRTP convenience methods
Derived &derived() {
return *static_cast<Derived*>(this);
}
const Derived &const_derived() const {
return *static_cast<const Derived*>(this);
}
protected:
const BinaryContext &BC;
/// Reference to the function being analysed
BinaryFunction &Func;
/// Tracks the state at basic block start (end) if direction of the dataflow
/// is forward (backward).
std::unordered_map<const BinaryBasicBlock *, StateTy> StateAtBBEntry;
/// Map a point to its previous (succeeding) point if the direction of the
/// dataflow is forward (backward). This is used to support convenience
/// methods to access the resulting state before (after) a given instruction,
/// otherwise our clients need to keep "prev" pointers themselves.
DenseMap<const MCInst *, ProgramPoint> PrevPoint;
/// Perform any bookkeeping before dataflow starts
void preflight() {
llvm_unreachable("Unimplemented method");
}
/// Sets initial state for each BB
StateTy getStartingStateAtBB(const BinaryBasicBlock &BB) {
llvm_unreachable("Unimplemented method");
}
/// Sets initial state for each instruction (out set)
StateTy getStartingStateAtPoint(const MCInst &Point) {
llvm_unreachable("Unimplemented method");
}
/// Computes the in set for the first instruction in a BB by applying the
/// confluence operator to the out sets of the last instruction of each pred
/// (in case of a backwards dataflow, we will operate on the in sets of each
/// successor to determine the starting state of the last instruction of the
/// current BB)
void doConfluence(StateTy &StateOut, const StateTy &StateIn) {
llvm_unreachable("Unimplemented method");
}
/// In case of a forwards dataflow, compute the in set for the first
/// instruction in a Landing Pad considering all out sets for associated
/// throw sites.
/// In case of a backwards dataflow, compute the in set of a invoke
/// instruction considering in sets for the first instructions of its
/// landing pads.
void doConfluenceWithLP(StateTy &StateOut, const StateTy &StateIn,
const MCInst &Invoke) {
return derived().doConfluence(StateOut, StateIn);
}
/// Returns the out set of an instruction given its in set.
/// If backwards, computes the in set given its out set.
StateTy computeNext(const MCInst &Point, const StateTy &Cur) {
llvm_unreachable("Unimplemented method");
return StateTy();
}
/// Returns the MCAnnotation name
StringRef getAnnotationName() const {
llvm_unreachable("Unimplemented method");
return StringRef("");
}
/// Private getter methods accessing state in a read-write fashion
StateTy &getOrCreateStateAt(const BinaryBasicBlock &BB) {
return StateAtBBEntry[&BB];
}
StateTy &getOrCreateStateAt(MCInst &Point) {
return BC.MIA->getOrCreateAnnotationAs<StateTy>(
BC.Ctx.get(), Point, derived().getAnnotationName());
}
StateTy &getOrCreateStateAt(ProgramPoint Point) {
if (Point.isBB())
return getOrCreateStateAt(*Point.getBB());
return getOrCreateStateAt(*Point.getInst());
}
public:
/// If the direction of the dataflow is forward, operates on the last
/// instruction of all predecessors when performing an iteration of the
/// dataflow equation for the start of this BB. If backwards, operates on
/// the first instruction of all successors.
void doForAllSuccsOrPreds(const BinaryBasicBlock &BB,
std::function<void(ProgramPoint)> Task) {
if (!Backward)
return doForAllPreds(BC, BB, Task);
return doForAllSuccs(BB, Task);
}
/// We need the current binary context and the function that will be processed
/// in this dataflow analysis.
DataflowAnalysis(const BinaryContext &BC, BinaryFunction &BF)
: BC(BC), Func(BF) {}
virtual ~DataflowAnalysis() {
cleanAnnotations();
}
/// Track the state at basic block start (end) if direction of the dataflow
/// is forward (backward).
ErrorOr<const StateTy &> getStateAt(const BinaryBasicBlock &BB) const {
auto Iter = StateAtBBEntry.find(&BB);
if (Iter == StateAtBBEntry.end())
return make_error_code(errc::result_out_of_range);
return Iter->second;
}
/// Track the state at the end (start) of each MCInst in this function if
/// the direction of the dataflow is forward (backward).
ErrorOr<const StateTy &> getStateAt(const MCInst &Point) const {
return BC.MIA->tryGetAnnotationAs<StateTy>(
Point, const_derived().getAnnotationName());
}
/// Return the out set (in set) of a given program point if the direction of
/// the dataflow is forward (backward).
ErrorOr<const StateTy &> getStateAt(ProgramPoint Point) const {
if (Point.isBB())
return getStateAt(*Point.getBB());
return getStateAt(*Point.getInst());
}
ErrorOr<const StateTy &> getStateBefore(const MCInst &Point) {
return getStateAt(PrevPoint[&Point]);
}
/// Return the in set (out set) of a given program point if the direction of
/// the dataflow is forward (backward).
ErrorOr<const StateTy &>getStateBefore(ProgramPoint Point) {
if (Point.isBB())
return getStateAt(*Point.getBB());
return getStateAt(PrevPoint[Point.getInst()]);
}
/// Remove any state annotations left by this analysis
void cleanAnnotations() {
for (auto &BB : Func) {
for (auto &Inst : BB) {
BC.MIA->removeAnnotation(Inst, derived().getAnnotationName());
}
}
}
/// Public entry point that will perform the entire analysis form start to
/// end.
void run() {
derived().preflight();
// Initialize state for all points of the function
for (auto &BB : Func) {
auto &St = getOrCreateStateAt(BB);
St = derived().getStartingStateAtBB(BB);
for (auto &Inst : BB) {
auto &St = getOrCreateStateAt(Inst);
St = derived().getStartingStateAtPoint(Inst);
}
}
assert(Func.begin() != Func.end() && "Unexpected empty function");
std::queue<BinaryBasicBlock *> Worklist;
// TODO: Pushing this in a DFS ordering will greatly speed up the dataflow
// performance.
if (!Backward) {
for (auto &BB : Func) {
Worklist.push(&BB);
MCInst *Prev = nullptr;
for (auto &Inst : BB) {
PrevPoint[&Inst] = Prev ? ProgramPoint(Prev) : ProgramPoint(&BB);
Prev = &Inst;
}
}
} else {
for (auto I = Func.rbegin(), E = Func.rend(); I != E; ++I) {
Worklist.push(&*I);
MCInst *Prev = nullptr;
for (auto J = (*I).rbegin(), E2 = (*I).rend(); J != E2; ++J) {
auto &Inst = *J;
PrevPoint[&Inst] = Prev ? ProgramPoint(Prev) : ProgramPoint(&*I);
Prev = &Inst;
}
}
}
// Main dataflow loop
while (!Worklist.empty()) {
auto *BB = Worklist.front();
Worklist.pop();
// Calculate state at the entry of first instruction in BB
StateTy StateAtEntry = getOrCreateStateAt(*BB);
if (BB->isLandingPad()) {
doForAllSuccsOrPreds(*BB, [&](ProgramPoint P) {
if (P.isInst() && BC.MIA->isInvoke(*P.getInst()))
derived().doConfluenceWithLP(StateAtEntry, *getStateAt(P),
*P.getInst());
else
derived().doConfluence(StateAtEntry, *getStateAt(P));
});
} else {
doForAllSuccsOrPreds(*BB, [&](ProgramPoint P) {
derived().doConfluence(StateAtEntry, *getStateAt(P));
});
}
bool Changed = false;
StateTy &St = getOrCreateStateAt(*BB);
if (St != StateAtEntry) {
Changed = true;
St = std::move(StateAtEntry);
}
// Propagate information from first instruction down to the last one
StateTy *PrevState = &St;
const MCInst *LAST = nullptr;
if (!Backward)
LAST = &*BB->rbegin();
else
LAST = &*BB->begin();
auto doNext = [&] (MCInst &Inst, const BinaryBasicBlock &BB) {
StateTy CurState = derived().computeNext(Inst, *PrevState);
if (Backward && BC.MIA->isInvoke(Inst)) {
auto *LBB = Func.getLandingPadBBFor(BB, Inst);
if (LBB) {
auto First = LBB->begin();
if (First != LBB->end()) {
derived().doConfluenceWithLP(CurState,
getOrCreateStateAt(&*First), Inst);
} else {
derived().doConfluenceWithLP(CurState, getOrCreateStateAt(LBB),
Inst);
}
}
}
StateTy &St = getOrCreateStateAt(Inst);
if (St != CurState) {
St = CurState;
if (&Inst == LAST)
Changed = true;
}
PrevState = &St;
};
if (!Backward) {
for (auto &Inst : *BB) {
doNext(Inst, *BB);
}
} else {
for (auto I = BB->rbegin(), E = BB->rend(); I != E; ++I) {
doNext(*I, *BB);
}
}
if (Changed) {
if (!Backward) {
for (auto Succ : BB->successors()) {
Worklist.push(Succ);
}
for (auto LandingPad : BB->landing_pads()) {
Worklist.push(LandingPad);
}
} else {
for (auto Pred : BB->predecessors()) {
Worklist.push(Pred);
}
for (auto Thrower : BB->throwers()) {
Worklist.push(Thrower);
}
}
}
} // end while (!Worklist.empty())
}
};
/// Define an iterator for navigating the expressions calculated by a
/// dataflow analysis at each program point, when they are backed by a
/// BitVector.
class ExprIterator
: public std::iterator<std::forward_iterator_tag, const MCInst *> {
const BitVector *BV;
const std::vector<const MCInst *> &Expressions;
int Idx;
public:
ExprIterator &operator++() {
assert(Idx != -1 && "Iterator already at the end");
Idx = BV->find_next(Idx);
return *this;
}
ExprIterator operator++(int) {
assert(Idx != -1 && "Iterator already at the end");
ExprIterator Ret = *this;
++(*this);
return Ret;
}
bool operator==(const ExprIterator &Other) const { return Idx == Other.Idx; }
bool operator!=(const ExprIterator &Other) const { return Idx != Other.Idx; }
const MCInst *operator*() {
assert(Idx != -1 && "Invalid access to end iterator");
return Expressions[Idx];
}
ExprIterator(const BitVector *BV, const std::vector<const MCInst *> &Exprs)
: BV(BV), Expressions(Exprs) {
Idx = BV->find_first();
}
ExprIterator(const BitVector *BV, const std::vector<const MCInst *> &Exprs,
int Idx)
: BV(BV), Expressions(Exprs), Idx(Idx) {}
int getBitVectorIndex() const {
return Idx;
}
};
/// Specialization of DataflowAnalysis whose state specifically stores
/// a set of instructions.
template <typename Derived, bool Backward = false>
class InstrsDataflowAnalysis
: public DataflowAnalysis<Derived, BitVector, Backward> {
public:
/// These iterator functions offer access to the set of pointers to
/// instructions in a given program point
template <typename T>
ExprIterator expr_begin(T &Point) const {
if (auto State = this->getStateAt(Point))
return ExprIterator(&*State, Expressions);
return expr_end();
}
ExprIterator expr_begin(BitVector &BV) const {
return ExprIterator(&BV, Expressions);
}
ExprIterator expr_end() const {
return ExprIterator(nullptr, Expressions, -1);
}
/// Used to size the set of expressions/definitions being tracked by the
/// dataflow analysis
uint64_t NumInstrs{0};
/// We put every MCInst we want to track (which one representing an
/// expression/def) into a vector because we need to associate them with
/// small numbers. They will be tracked via BitVectors throughout the
/// dataflow analysis.
std::vector<const MCInst *> Expressions;
/// Maps expressions defs (MCInsts) to its index in the Expressions vector
std::unordered_map<const MCInst *, uint64_t> ExprToIdx;
InstrsDataflowAnalysis(const BinaryContext &BC, BinaryFunction &BF)
: DataflowAnalysis<Derived, BitVector, Backward>(BC, BF) {}
virtual ~InstrsDataflowAnalysis() {}
};
} // namespace bolt
/// DenseMapInfo allows us to use the DenseMap LLVM data structure to store
/// ProgramPoints.
template<> struct DenseMapInfo<bolt::ProgramPoint> {
static inline bolt::ProgramPoint getEmptyKey() {
uintptr_t Val = static_cast<uintptr_t>(-1);
Val <<= PointerLikeTypeTraits<MCInst*>::NumLowBitsAvailable;
return bolt::ProgramPoint(reinterpret_cast<MCInst*>(Val));
}
static inline bolt::ProgramPoint getTombstoneKey() {
uintptr_t Val = static_cast<uintptr_t>(-2);
Val <<= PointerLikeTypeTraits<MCInst*>::NumLowBitsAvailable;
return bolt::ProgramPoint(reinterpret_cast<MCInst*>(Val));
}
static unsigned getHashValue(const bolt::ProgramPoint &PP) {
return (unsigned((uintptr_t)PP.Data.BB) >> 4) ^
(unsigned((uintptr_t)PP.Data.BB) >> 9);
}
static bool isEqual(const bolt::ProgramPoint &LHS,
const bolt::ProgramPoint &RHS) {
return LHS.Data.BB == RHS.Data.BB;
}
};
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
const BitVector &Val);
} // namespace llvm
#endif

View File

@ -0,0 +1,170 @@
//===--- Passes/DataflowInfoManager.cpp -----------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//===----------------------------------------------------------------------===//
#include "DataflowInfoManager.h"
namespace llvm {
namespace bolt {
ReachingDefOrUse</*Def=*/true> &DataflowInfoManager::getReachingDefs() {
if (RD)
return *RD;
assert(FA && "FrameAnalysis required");
RD.reset(new ReachingDefOrUse<true>(*FA, BC, BF));
{
NamedRegionTimer T1("RD", "Dataflow", true);
RD->run();
}
return *RD;
}
void DataflowInfoManager::invalidateReachingDefs() {
RD.reset(nullptr);
}
ReachingDefOrUse</*Def=*/false> &DataflowInfoManager::getReachingUses() {
if (RU)
return *RU;
assert(FA && "FrameAnalysis required");
RU.reset(new ReachingDefOrUse<false>(*FA, BC, BF));
{
NamedRegionTimer T1("RU", "Dataflow", true);
RU->run();
}
return *RU;
}
void DataflowInfoManager::invalidateReachingUses() {
RU.reset(nullptr);
}
LivenessAnalysis &DataflowInfoManager::getLivenessAnalysis() {
if (LA)
return *LA;
assert(FA && "FrameAnalysis required");
LA.reset(new LivenessAnalysis(*FA, BC, BF));
{
NamedRegionTimer T1("LA", "Dataflow", true);
LA->run();
}
return *LA;
}
void DataflowInfoManager::invalidateLivenessAnalysis() {
LA.reset(nullptr);
}
DominatorAnalysis<false> &DataflowInfoManager::getDominatorAnalysis() {
if (DA)
return *DA;
DA.reset(new DominatorAnalysis<false>(BC, BF));
{
NamedRegionTimer T1("DA", "Dataflow", true);
DA->run();
}
return *DA;
}
void DataflowInfoManager::invalidateDominatorAnalysis() {
DA.reset(nullptr);
}
DominatorAnalysis<true> &DataflowInfoManager::getPostDominatorAnalysis() {
if (PDA)
return *PDA;
PDA.reset(new DominatorAnalysis<true>(BC, BF));
{
NamedRegionTimer T1("PDA", "Dataflow", true);
PDA->run();
}
return *PDA;
}
void DataflowInfoManager::invalidatePostDominatorAnalysis() {
PDA.reset(nullptr);
}
StackPointerTracking &DataflowInfoManager::getStackPointerTracking() {
if (SPT)
return *SPT;
SPT.reset(new StackPointerTracking(BC, BF));
{
NamedRegionTimer T1("SPT", "Dataflow", true);
SPT->run();
}
return *SPT;
}
void DataflowInfoManager::invalidateStackPointerTracking() {
SPT.reset(nullptr);
}
ReachingInsns<false> &DataflowInfoManager::getReachingInsns() {
if (RI)
return *RI;
RI.reset(new ReachingInsns<false>(BC, BF));
{
NamedRegionTimer T1("RI", "Dataflow", true);
RI->run();
}
return *RI;
}
void DataflowInfoManager::invalidateReachingInsns() {
RI.reset(nullptr);
}
ReachingInsns<true> &DataflowInfoManager::getReachingInsnsBackwards() {
if (RIB)
return *RIB;
RIB.reset(new ReachingInsns<true>(BC, BF));
{
NamedRegionTimer T1("RIB", "Dataflow", true);
RIB->run();
}
return *RIB;
}
void DataflowInfoManager::invalidateReachingInsnsBackwards() {
RIB.reset(nullptr);
}
std::unordered_map<const MCInst *, BinaryBasicBlock *> &
DataflowInfoManager::getInsnToBBMap() {
if (InsnToBB)
return *InsnToBB;
InsnToBB.reset(new std::unordered_map<const MCInst *, BinaryBasicBlock *>());
for (auto &BB : BF) {
for (auto &Inst : BB)
(*InsnToBB)[&Inst] = &BB;
}
return *InsnToBB;
}
void DataflowInfoManager::invalidateInsnToBBMap() {
InsnToBB.reset(nullptr);
}
void DataflowInfoManager::invalidateAll() {
invalidateReachingDefs();
invalidateReachingUses();
invalidateLivenessAnalysis();
invalidateDominatorAnalysis();
invalidatePostDominatorAnalysis();
invalidateStackPointerTracking();
invalidateReachingInsns();
invalidateReachingInsnsBackwards();
invalidateInsnToBBMap();
}
} // end namespace bolt
} // end namespace llvm

View File

@ -0,0 +1,72 @@
//===--- Passes/DataflowInfoManager.h -------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_LLVM_BOLT_PASSES_DATAFLOWINFOMANAGER_H
#define LLVM_TOOLS_LLVM_BOLT_PASSES_DATAFLOWINFOMANAGER_H
#include "FrameAnalysis.h"
#include "ReachingDefOrUse.h"
#include "DominatorAnalysis.h"
#include "StackPointerTracking.h"
#include "ReachingInsns.h"
#include "LivenessAnalysis.h"
namespace llvm {
namespace bolt {
/// Manages instances for dataflow analyses and try to preserve the data
/// calculated by each analysis as much as possible, saving the need to
/// recompute it. Also provide an interface for data invalidation when the
/// analysis is outdated after a transform pass modified the function.
class DataflowInfoManager {
const FrameAnalysis *FA;
const BinaryContext &BC;
BinaryFunction &BF;
std::unique_ptr<ReachingDefOrUse</*Def=*/true>> RD;
std::unique_ptr<ReachingDefOrUse</*Def=*/false>> RU;
std::unique_ptr<LivenessAnalysis> LA;
std::unique_ptr<DominatorAnalysis</*Bwd=*/false>> DA;
std::unique_ptr<DominatorAnalysis</*Bwd=*/true>> PDA;
std::unique_ptr<StackPointerTracking> SPT;
std::unique_ptr<ReachingInsns<false>> RI;
std::unique_ptr<ReachingInsns<true>> RIB;
std::unique_ptr<std::unordered_map<const MCInst *, BinaryBasicBlock *>>
InsnToBB;
public:
DataflowInfoManager(const FrameAnalysis *FA, const BinaryContext &BC,
BinaryFunction &BF) : FA(FA), BC(BC), BF(BF) {};
ReachingDefOrUse</*Def=*/true> &getReachingDefs();
void invalidateReachingDefs();
ReachingDefOrUse</*Def=*/false> &getReachingUses();
void invalidateReachingUses();
LivenessAnalysis &getLivenessAnalysis();
void invalidateLivenessAnalysis();
DominatorAnalysis<false> &getDominatorAnalysis();
void invalidateDominatorAnalysis();
DominatorAnalysis<true> &getPostDominatorAnalysis();
void invalidatePostDominatorAnalysis();
StackPointerTracking &getStackPointerTracking();
void invalidateStackPointerTracking();
ReachingInsns<false> &getReachingInsns();
void invalidateReachingInsns();
ReachingInsns<true> &getReachingInsnsBackwards();
void invalidateReachingInsnsBackwards();
std::unordered_map<const MCInst *, BinaryBasicBlock *> &getInsnToBBMap();
void invalidateInsnToBBMap();
void invalidateAll();
};
} // end namespace bolt
} // end namespace llvm
#endif

View File

@ -0,0 +1,141 @@
//===--- Passes/DominatorAnalysis.h ---------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_LLVM_BOLT_PASSES_DOMINATORANALYSIS_H
#define LLVM_TOOLS_LLVM_BOLT_PASSES_DOMINATORANALYSIS_H
#include "DataflowAnalysis.h"
namespace llvm {
namespace bolt {
/// The whole reason for running a dominator analysis at the instruction level
/// (that is much more expensive than at the BB level) is because of invoke
/// instructions that may cause early exits in the middle of the BB, making half
/// of the BB potentially dominate the landing pad but not instructions after
/// the invoke.
template <bool Backward = false>
class DominatorAnalysis
: public InstrsDataflowAnalysis<DominatorAnalysis<Backward>, Backward> {
friend class DataflowAnalysis<DominatorAnalysis<Backward>, BitVector,
Backward>;
public:
DominatorAnalysis(const BinaryContext &BC, BinaryFunction &BF)
: InstrsDataflowAnalysis<DominatorAnalysis<Backward>, Backward>(BC, BF) {}
virtual ~DominatorAnalysis() {}
SmallVector<ProgramPoint, 4> getDominanceFrontierFor(const MCInst &Dom) {
SmallVector<ProgramPoint, 4> Result;
auto DomIdx = this->ExprToIdx[&Dom];
assert(!Backward && "Post-dom frontier not implemented");
for (auto &BB : this->Func) {
bool HasDominatedPred = false;
bool HasNonDominatedPred = false;
SmallVector<ProgramPoint, 4> Candidates;
this->doForAllSuccsOrPreds(BB, [&](ProgramPoint P) {
if ((*this->getStateAt(P))[DomIdx]) {
Candidates.emplace_back(P);
HasDominatedPred = true;
return;
}
HasNonDominatedPred = true;
});
if (HasDominatedPred && HasNonDominatedPred)
Result.append(Candidates.begin(), Candidates.end());
if ((*this->getStateAt(ProgramPoint::getLastPointAt(BB)))[DomIdx] &&
BB.succ_begin() == BB.succ_end())
Result.emplace_back(ProgramPoint::getLastPointAt(BB));
}
std::sort(Result.begin(), Result.end());
Result.erase(std::unique(Result.begin(), Result.end()), Result.end());
return Result;
}
bool doesADominatesB(const MCInst &A, const MCInst &B) {
return (*this->getStateAt(B))[this->ExprToIdx[&A]];
}
bool doesADominatesB(ProgramPoint A, const MCInst &B) {
if (A.isInst())
return doesADominatesB(*A.getInst(), B);
// This analysis keep track of which instructions dominates another
// instruction, it doesn't keep track of BBs. So we need a non-empty
// BB if we want to know whether this BB dominates something.
BinaryBasicBlock *BB = A.getBB();
while (BB->size() == 0) {
if (BB->succ_size() == 0)
return false;
assert (BB->succ_size() == 1);
BB = *BB->succ_begin();
}
const MCInst &InstA = *BB->begin();
return doesADominatesB(InstA, B);
}
void doForAllDominators(const MCInst &Inst,
std::function<void(const MCInst &)> Task) {
for (auto I = this->expr_begin(Inst), E = this->expr_end(); I != E; ++I) {
Task(**I);
}
}
private:
void preflight() {
// Populate our universe of tracked expressions with all instructions
// except pseudos
for (auto &BB : this->Func) {
for (auto &Inst : BB) {
this->Expressions.push_back(&Inst);
this->ExprToIdx[&Inst] = this->NumInstrs++;
}
}
}
BitVector getStartingStateAtBB(const BinaryBasicBlock &BB) {
// Entry points start with empty set
// All others start with the full set.
if (!Backward && BB.pred_size() == 0 && BB.throw_size() == 0)
return BitVector(this->NumInstrs, false);
if (Backward && BB.succ_size() == 0)
return BitVector(this->NumInstrs, false);
return BitVector(this->NumInstrs, true);
}
BitVector getStartingStateAtPoint(const MCInst &Point) {
return BitVector(this->NumInstrs, true);
}
void doConfluence(BitVector &StateOut, const BitVector &StateIn) {
StateOut &= StateIn;
}
BitVector computeNext(const MCInst &Point, const BitVector &Cur) {
BitVector Next = Cur;
// Gen
if (!this->BC.MIA->isCFI(Point)) {
Next.set(this->ExprToIdx[&Point]);
}
return Next;
}
StringRef getAnnotationName() const {
if (Backward)
return StringRef("PostDominatorAnalysis");
return StringRef("DominatorAnalysis");
}
};
} // end namespace bolt
} // end namespace llvm
#endif

View File

@ -0,0 +1,703 @@
//===--- Passes/FrameAnalysis.h -------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//===----------------------------------------------------------------------===//
#include "FrameAnalysis.h"
#include <fstream>
#define DEBUG_TYPE "fa"
using namespace llvm;
namespace opts {
extern cl::opt<unsigned> Verbosity;
extern bool shouldProcess(const bolt::BinaryFunction &Function);
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"));
bool shouldFrameOptimize(const llvm::bolt::BinaryFunction &Function) {
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 = "";
}
bool IsValid = true;
if (!FrameOptFunctionNames.empty()) {
IsValid = false;
for (auto &Name : FrameOptFunctionNames) {
if (Function.hasName(Name)) {
IsValid = true;
break;
}
}
}
if (!IsValid)
return false;
return IsValid;
}
} // 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: " << 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};
FrameIndexEntry FIE;
bool decodeFrameAccess(const MCInst &Inst) {
int32_t SrcImm{0};
MCPhysReg Reg{0};
int64_t StackOffset{0};
bool IsIndexed{false};
if (!BC.MIA->isStackAccess(
Inst, FIE.IsLoad, FIE.IsStore, FIE.IsStoreFromReg, Reg, SrcImm,
FIE.StackPtrReg, StackOffset, FIE.Size, FIE.IsSimple, IsIndexed)) {
return true;
}
if (IsIndexed) {
DEBUG(dbgs() << "Giving up on indexed memory access in the frame\n");
return false;
}
assert(FIE.Size != 0);
FIE.RegOrImm = SrcImm;
if (FIE.IsLoad || FIE.IsStoreFromReg)
FIE.RegOrImm = Reg;
if (FIE.StackPtrReg == BC.MIA->getStackPointer() && SPOffset != SPT.EMPTY &&
SPOffset != SPT.SUPERPOSITION) {
DEBUG(dbgs() << "Adding access via SP while CFA reg is another one\n");
FIE.StackOffset = SPOffset + StackOffset;
} else if (FIE.StackPtrReg == BC.MIA->getFramePointer() &&
FPOffset != SPT.EMPTY && FPOffset != SPT.SUPERPOSITION) {
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 {
DEBUG(dbgs() << "Found stack access with reg different than cfa reg.\n");
DEBUG(dbgs() << "\tCurrent CFA reg: " << CfaReg
<< "\n\tStack access reg: " << FIE.StackPtrReg << "\n");
DEBUG(dbgs() << "Blame insn: ");
DEBUG(Inst.dump());
return false;
}
IsValidAccess = true;
return true;
}
public:
FrameAccessAnalysis(const BinaryContext &BC, BinaryFunction &BF)
: SPT(BC, BF), BC(BC), BF(BF) {
{
NamedRegionTimer T1("SPT", "Dataflow", true);
SPT.run();
}
}
void enterNewBB() { Prev = nullptr; }
const FrameIndexEntry &getFIE() const { return FIE; }
int getSPOffset() const { return SPOffset; }
bool isValidAccess() const { return IsValidAccess; }
bool doNext(const BinaryBasicBlock &BB, const MCInst &Inst) {
IsValidAccess = 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.MIA->isCFI(Inst)) {
const auto *CFI = BF.getCFIFor(Inst);
switch (CFI->getOperation()) {
case MCCFIInstruction::OpDefCfa:
CfaOffset = CFI->getOffset();
// Fall-through
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");
auto &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.MIA->leaksStackAddress(Inst, *BC.MRI, SPT.HasFramePointer)) {
DEBUG(dbgs() << "Leaked stack address, giving up on this function.\n");
DEBUG(dbgs() << "Blame insn: ");
DEBUG(Inst.dump());
return false;
}
return decodeFrameAccess(Inst);
}
};
} // end anonymous namespace
void FrameAnalysis::addArgAccessesFor(const BinaryContext &BC, MCInst &Inst,
ArgAccesses &&AA) {
if (AA.AssumeEverything) {
// Index 0 in ArgAccessesVector represents an "assumeeverything" entry
BC.MIA->addAnnotation(BC.Ctx.get(), Inst, "ArgAccessEntry", 0U);
return;
}
BC.MIA->addAnnotation(BC.Ctx.get(), Inst, "ArgAccessEntry",
(unsigned)ArgAccessesVector.size());
ArgAccessesVector.emplace_back(AA);
}
void FrameAnalysis::addArgInStackAccessFor(const BinaryContext &BC,
MCInst &Inst,
const ArgInStackAccess &Arg) {
auto AA = getArgAccessesFor(BC, Inst);
if (!AA) {
addArgAccessesFor(BC, Inst, ArgAccesses(false));
AA = getArgAccessesFor(BC, Inst);
assert(AA && "Object setup failed");
}
auto &Set = AA->Set;
assert(!AA->AssumeEverything && "Adding arg to AssumeEverything set");
Set.emplace(Arg);
}
void FrameAnalysis::addFIEFor(const BinaryContext &BC, MCInst &Inst,
const FrameIndexEntry &FIE) {
BC.MIA->addAnnotation(BC.Ctx.get(), Inst, "FrameAccessEntry",
(unsigned)FIEVector.size());
FIEVector.emplace_back(FIE);
}
ErrorOr<ArgAccesses &>
FrameAnalysis::getArgAccessesFor(const BinaryContext &BC, const MCInst &Inst) {
if (auto Idx = BC.MIA->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 BinaryContext &BC,
const MCInst &Inst) const {
if (auto Idx = BC.MIA->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 BinaryContext &BC, const MCInst &Inst) const {
if (auto Idx =
BC.MIA->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::buildCallGraph(BinaryContext &BC,
std::map<uint64_t, BinaryFunction> &BFs) {
for (auto &I : BFs) {
BinaryFunction &Caller = I.second;
Functions.emplace(&Caller);
for (BinaryBasicBlock &BB : Caller) {
for (MCInst &Inst : BB) {
if (!BC.MIA->isCall(Inst))
continue;
auto *TargetSymbol = BC.MIA->getTargetSymbol(Inst);
if (!TargetSymbol) {
// This is an indirect call, we cannot record a target.
continue;
}
auto *Function = BC.getFunctionForSymbol(TargetSymbol);
if (!Function) {
// Call to a function without a BinaryFunction object.
continue;
}
// Create a new edge in the call graph
CallGraphEdges[&Caller].emplace_back(Function);
ReverseCallGraphEdges[Function].emplace_back(&Caller);
}
}
}
}
void FrameAnalysis::buildCGTraversalOrder() {
enum NodeStatus { NEW, VISITING, VISITED };
std::unordered_map<const BinaryFunction *, NodeStatus> NodeStatus;
std::stack<BinaryFunction *> Worklist;
for (auto *Func : Functions) {
Worklist.push(Func);
NodeStatus[Func] = NEW;
}
while (!Worklist.empty()) {
auto *Func = Worklist.top();
Worklist.pop();
if (NodeStatus[Func] == VISITED)
continue;
if (NodeStatus[Func] == VISITING) {
TopologicalCGOrder.push_back(Func);
NodeStatus[Func] = VISITED;
continue;
}
assert(NodeStatus[Func] == NEW);
NodeStatus[Func] = VISITING;
Worklist.push(Func);
for (auto *Callee : CallGraphEdges[Func]) {
if (NodeStatus[Callee] == VISITING || NodeStatus[Callee] == VISITED)
continue;
Worklist.push(Callee);
}
}
}
void FrameAnalysis::getInstClobberList(const BinaryContext &BC,
const MCInst &Inst,
BitVector &KillSet) const {
if (!BC.MIA->isCall(Inst)) {
BC.MIA->getClobberedRegs(Inst, KillSet, *BC.MRI);
return;
}
const auto *TargetSymbol = BC.MIA->getTargetSymbol(Inst);
// If indirect call, kill set should have all elements
if (TargetSymbol == nullptr) {
KillSet.set(0, KillSet.size());
return;
}
const auto *Function = BC.getFunctionForSymbol(TargetSymbol);
if (Function == nullptr) {
// Call to a function without a BinaryFunction object.
// This should be a call to a PLT entry, and since it is a trampoline to
// a DSO, we can't really know the code in advance. Conservatively assume
// everything is clobbered.
KillSet.set(0, KillSet.size());
return;
}
auto BV = RegsKilledMap.find(Function);
if (BV != RegsKilledMap.end()) {
KillSet |= BV->second;
return;
}
// Ignore calls to function whose clobber list wasn't yet calculated. This
// instruction will be evaluated again once we have info for the callee.
return;
}
BitVector FrameAnalysis::getFunctionClobberList(const BinaryContext &BC,
const BinaryFunction *Func) {
BitVector RegsKilled = BitVector(BC.MRI->getNumRegs(), false);
if (!Func->isSimple() || !Func->hasCFG()) {
RegsKilled.set(0, RegsKilled.size());
return RegsKilled;
}
for (const auto &BB : *Func) {
for (const auto &Inst : BB) {
getInstClobberList(BC, Inst, RegsKilled);
}
}
return RegsKilled;
}
void FrameAnalysis::buildClobberMap(const BinaryContext &BC) {
std::queue<BinaryFunction *> Queue;
for (auto *Func : TopologicalCGOrder) {
Queue.push(Func);
}
while (!Queue.empty()) {
auto *Func = Queue.front();
Queue.pop();
BitVector RegsKilled = getFunctionClobberList(BC, Func);
bool Updated = computeArgsAccessed(BC, *Func);
if (RegsKilledMap.find(Func) == RegsKilledMap.end()) {
RegsKilledMap[Func] = std::move(RegsKilled);
continue;
}
if (RegsKilledMap[Func] != RegsKilled || Updated) {
for (auto Caller : ReverseCallGraphEdges[Func]) {
Queue.push(Caller);
}
}
RegsKilledMap[Func] = std::move(RegsKilled);
}
if (opts::Verbosity == 0 && (!DebugFlag || !isCurrentDebugType("fa")))
return;
// This loop is for computing statistics only
for (auto *Func : TopologicalCGOrder) {
auto Iter = RegsKilledMap.find(Func);
assert(Iter != RegsKilledMap.end() &&
"Failed to compute all clobbers list");
if (Iter->second.all()) {
auto Count = Func->getExecutionCount();
if (Count != BinaryFunction::COUNT_NO_PROFILE)
CountFunctionsAllClobber += Count;
++NumFunctionsAllClobber;
}
if (!DebugFlag || !isCurrentDebugType("fa"))
continue;
// DEBUG only
dbgs() << "Killed regs set for func: " << Func->getPrintName() << "\n";
const BitVector &RegsKilled = Iter->second;
int RegIdx = RegsKilled.find_first();
while (RegIdx != -1) {
dbgs() << "\tREG" << RegIdx;
RegIdx = RegsKilled.find_next(RegIdx);
};
dbgs() << "\n";
}
}
bool FrameAnalysis::updateArgsTouchedFor(const BinaryContext &BC,
const BinaryFunction &BF, MCInst &Inst,
int CurOffset) {
if (!BC.MIA->isCall(Inst))
return false;
std::set<int64_t> Res;
const auto *TargetSymbol = BC.MIA->getTargetSymbol(Inst);
// If indirect call, we conservatively assume it accesses all stack positions
if (TargetSymbol == nullptr) {
addArgAccessesFor(BC, Inst, ArgAccesses(/*AssumeEverything=*/true));
bool Updated{false};
if (!FunctionsRequireAlignment.count(&BF)) {
Updated = true;
FunctionsRequireAlignment.insert(&BF);
}
return Updated;
}
const auto *Function = BC.getFunctionForSymbol(TargetSymbol);
// Call to a function without a BinaryFunction object. Conservatively assume
// it accesses all stack positions
if (Function == nullptr) {
addArgAccessesFor(BC, Inst, ArgAccesses(/*AssumeEverything=*/true));
bool Updated{false};
if (!FunctionsRequireAlignment.count(&BF)) {
Updated = true;
FunctionsRequireAlignment.insert(&BF);
}
return Updated;
}
auto Iter = ArgsTouchedMap.find(Function);
if (Iter == ArgsTouchedMap.end())
return false;
bool Changed = false;
if (BC.MIA->isTailCall(Inst)) {
// Ignore checking CurOffset because we can't always reliably determine the
// offset specially after an epilogue, where tailcalls happen. It should be
// -8.
for (auto Elem : Iter->second) {
if (ArgsTouchedMap[&BF].find(Elem) == ArgsTouchedMap[&BF].end()) {
ArgsTouchedMap[&BF].emplace(Elem);
Changed = true;
}
}
}
if (FunctionsRequireAlignment.count(Function) &&
!FunctionsRequireAlignment.count(&BF)) {
Changed = true;
FunctionsRequireAlignment.insert(&BF);
}
if (CurOffset == StackPointerTracking::EMPTY ||
CurOffset == StackPointerTracking::SUPERPOSITION) {
addArgAccessesFor(BC, Inst, ArgAccesses(/*AssumeEverything=*/true));
return Changed;
}
for (auto Elem : Iter->second) {
if (Elem.first == -1) {
addArgAccessesFor(BC, Inst, ArgAccesses(/*AssumeEverything=*/true));
break;
}
DEBUG(dbgs() << "Added arg in stack access annotation "
<< CurOffset + Elem.first << "\n");
addArgInStackAccessFor(
BC, Inst, ArgInStackAccess{/*StackOffset=*/CurOffset + Elem.first,
/*Size=*/Elem.second});
}
return Changed;
}
bool FrameAnalysis::computeArgsAccessed(const BinaryContext &BC,
BinaryFunction &BF) {
if (!BF.isSimple() || !BF.hasCFG()) {
DEBUG(dbgs() << "Treating " << BF.getPrintName() << " conservatively.\n");
bool Updated = false;
ArgsTouchedMap[&BF].emplace(std::make_pair(-1, 0));
if (!FunctionsRequireAlignment.count(&BF)) {
Updated = true;
FunctionsRequireAlignment.insert(&BF);
}
return Updated;
}
bool UpdatedArgsTouched = false;
FrameAccessAnalysis FAA(BC, BF);
for (auto BB : BF.layout()) {
FAA.enterNewBB();
for (auto &Inst : *BB) {
if (!FAA.doNext(*BB, Inst)) {
ArgsTouchedMap[&BF].emplace(std::make_pair(-1, 0));
break;
}
// Check for calls -- attach stack accessing info to them regarding their
// target
if (updateArgsTouchedFor(BC, 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;
DEBUG({
dbgs() << "Arg access offset " << FIE.StackOffset << " added to:\n";
BC.printInstruction(dbgs(), Inst, 0, &BF, true);
});
}
}
if (FunctionsRequireAlignment.count(&BF))
return UpdatedArgsTouched;
bool UpdatedAlignedStatus = false;
for (auto &BB : BF) {
if (UpdatedAlignedStatus)
break;
for (auto &Inst : BB) {
if (BC.MIA->requiresAlignedAddress(Inst)) {
if (!FunctionsRequireAlignment.count(&BF)) {
UpdatedAlignedStatus = true;
FunctionsRequireAlignment.insert(&BF);
break;
}
}
}
}
return UpdatedArgsTouched || UpdatedAlignedStatus;
}
bool FrameAnalysis::restoreFrameIndex(const BinaryContext &BC,
BinaryFunction &BF) {
FrameAccessAnalysis FAA(BC, BF);
DEBUG(dbgs() << "Restoring frame indices for \"" << BF.getPrintName()
<< "\"\n");
for (auto BB : BF.layout()) {
DEBUG(dbgs() << "\tNow at BB " << BB->getName() << "\n");
FAA.enterNewBB();
for (auto &Inst : *BB) {
if (!FAA.doNext(*BB, Inst))
return false;
DEBUG({
dbgs() << "\t\tNow at ";
Inst.dump();
dbgs() << "\t\t\tSP offset is " << FAA.getSPOffset() << "\n";
});
if (!FAA.isValidAccess())
continue;
const FrameIndexEntry &FIE = FAA.getFIE();
addFIEFor(BC, Inst, FIE);
DEBUG({
dbgs() << "Frame index annotation " << FIE << " added to:\n";
BC.printInstruction(dbgs(), Inst, 0, &BF, true);
});
}
}
return true;
}
void FrameAnalysis::cleanAnnotations(const BinaryContext &BC,
std::map<uint64_t, BinaryFunction> &BFs) {
for (auto &I : BFs) {
for (auto &BB : I.second) {
for (auto &Inst : BB) {
BC.MIA->removeAnnotation(Inst, "ArgAccessEntry");
BC.MIA->removeAnnotation(Inst, "FrameAccessEntry");
}
}
}
}
void FrameAnalysis::runOnFunctions(BinaryContext &BC,
std::map<uint64_t, BinaryFunction> &BFs,
std::set<uint64_t> &) {
{
NamedRegionTimer T1("Callgraph construction", "FOP breakdown", true);
buildCallGraph(BC, BFs);
}
{
NamedRegionTimer T1("build cg traversal order", "FOP breakdown", true);
buildCGTraversalOrder();
}
{
NamedRegionTimer T1("build clobber map", "FOP breakdown", true);
buildClobberMap(BC);
}
for (auto &I : BFs) {
auto Count = I.second.getExecutionCount();
if (Count != BinaryFunction::COUNT_NO_PROFILE)
CountDenominator += Count;
// "shouldOptimize" for passes that run after finalize
if (!(I.second.isSimple() && I.second.hasCFG() &&
opts::shouldProcess(I.second) && (I.second.getSize() > 0)) ||
!opts::shouldFrameOptimize(I.second)) {
++NumFunctionsNotOptimized;
if (Count != BinaryFunction::COUNT_NO_PROFILE)
CountFunctionsNotOptimized += Count;
continue;
}
{
NamedRegionTimer T1("restore frame index", "FOP breakdown", true);
if (!restoreFrameIndex(BC, I.second)) {
++NumFunctionsFailedRestoreFI;
auto Count = I.second.getExecutionCount();
if (Count != BinaryFunction::COUNT_NO_PROFILE)
CountFunctionsFailedRestoreFI += Count;
continue;
}
}
AnalyzedFunctions.insert(&I.second);
}
}
void FrameAnalysis::printStats() {
outs() << "BOLT-INFO FRAME ANALYSIS: Number of functions conservatively "
"treated as clobbering all registers: "
<< NumFunctionsAllClobber
<< format(" (%.1lf%% dyn cov)\n",
(100.0 * CountFunctionsAllClobber / CountDenominator))
<< "BOLT-INFO FRAME ANALYSIS: " << NumFunctionsNotOptimized
<< " function(s) "
<< format("(%.1lf%% dyn cov)",
(100.0 * CountFunctionsNotOptimized / CountDenominator))
<< " 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";
}
} // namespace bolt
} // namespace llvm

262
bolt/Passes/FrameAnalysis.h Normal file
View File

@ -0,0 +1,262 @@
//===--- Passes/FrameAnalysis.h -------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_LLVM_BOLT_PASSES_FRAMEANALYSIS_H
#define LLVM_TOOLS_LLVM_BOLT_PASSES_FRAMEANALYSIS_H
#include "BinaryPasses.h"
#include "StackPointerTracking.h"
namespace llvm {
namespace bolt {
/// Alias analysis information attached to each instruction that accesses a
/// frame position. This is called a "frame index" by LLVM Target libs when
/// it is building a MachineFunction frame, and we use the same name here
/// because we are essentially doing the job of frame reconstruction.
struct FrameIndexEntry {
/// If both IsLoad and IsStore are set, it means this is an instruction that
/// reads and updates this frame location.
bool IsLoad;
bool IsStore;
/// If a store, this controls whether the store uses a register os an imm
/// as the source value.
bool IsStoreFromReg;
/// If load, this holds the destination register. If store, this holds
/// either the source register or source immediate.
int32_t RegOrImm;
/// StackOffset and Size are the two aspects that identify this frame access
/// for the purposes of alias analysis.
int64_t StackOffset;
uint8_t Size;
/// If this is false, we will never atempt to remove or optimize this
/// instruction. We just use it to keep track of stores we don't fully
/// understand but we know it may write to a frame position.
bool IsSimple;
uint16_t StackPtrReg;
};
/// Record an access to an argument in stack. This should be attached to
/// call instructions, so StackOffset and Size are determined in the context
/// of the caller. This information helps the caller understand how the callee
/// may access its private stack.
struct ArgInStackAccess {
int64_t StackOffset;
uint8_t Size;
bool operator<(const ArgInStackAccess &RHS) const {
if (StackOffset != RHS.StackOffset)
return StackOffset < RHS.StackOffset;
return Size < RHS.Size;
}
};
/// The set of all args-in-stack accesses for a given instruction. If
/// AssumeEverything is true, then the set should be ignored and the
/// corresponding instruction should be treated as accessing the entire
/// stack for the purposes of analysis and optimization.
struct ArgAccesses {
bool AssumeEverything;
std::set<ArgInStackAccess> Set;
explicit ArgAccesses(bool AssumeEverything)
: AssumeEverything(AssumeEverything) {}
};
raw_ostream &operator<<(raw_ostream &OS,
const FrameIndexEntry &FIE);
/// This pass attaches stack access information to instructions. If a load/store
/// instruction accesses a stack position, it will identify the CFA offset and
/// size information of this access, where CFA is the Canonical Frame Address
/// (using DWARF terminology).
///
/// This pass also computes frame usage information obtained by a bottom-up call
/// graph traversal: which registers are clobbered by functions (including their
/// callees as determined by the call graph), whether a function accesses its
/// caller's stack frame and whether a function demands its stack to be aligned
/// due to the use of SSE aligned load/store operations present in itself or any
/// of its direct or indirect callees.
///
/// Initialization:
///
/// FrameAnalysis FA(PrintPass);
/// RA.runOnFunctions(BC, BFs, LargeFunctions);
///
/// Usage (fetching frame access information about a given instruction):
///
/// auto FIE = FA.getFIEFor(BC, Instruction);
/// if (FIE && FIE->IsSimple) {
/// ... = FIE->StackOffset
/// ... = FIE->Size
/// }
///
/// Usage (determining the set of stack positions accessed by the target of a
/// call:
///
/// auto Args = FA.getArgAccessesFor(BC, CallInst);
/// if (Args && Args->AssumeEverything) {
/// ... callee may access any position of our current stack frame
/// }
///
class FrameAnalysis : public BinaryFunctionPass {
/// Call graph info
/// The set of functions analyzed by our call graph
std::set<BinaryFunction *> Functions;
/// Model the "function calls function" edges
std::map<const BinaryFunction *, std::vector<BinaryFunction *>>
CallGraphEdges;
/// Model the "function called by function" edges
std::map<const BinaryFunction *, std::vector<BinaryFunction *>>
ReverseCallGraphEdges;
/// DFS or reverse post-ordering of the call graph nodes to allow us to
/// traverse the call graph bottom-up
std::deque<BinaryFunction *> TopologicalCGOrder;
/// Map functions to the set of registers they may overwrite starting at when
/// it is called until it returns to the caller.
std::map<const BinaryFunction *, BitVector> RegsKilledMap;
/// Map functions to the set of <stack offsets, size> tuples representing
/// accesses to stack positions that belongs to caller
std::map<const BinaryFunction *, std::set<std::pair<int64_t, uint8_t>>>
ArgsTouchedMap;
/// The set of functions we were able to perform the full analysis up to
/// restoring frame indexes for all load/store instructions.
DenseSet<const BinaryFunction *> AnalyzedFunctions;
/// Set of functions that require the stack to be 16B aligned
DenseSet<const BinaryFunction *> FunctionsRequireAlignment;
/// Owns ArgAccesses for all instructions. References to elements are
/// attached to instructions as indexes to this vector, in MCAnnotations.
std::vector<ArgAccesses> ArgAccessesVector;
/// Same for FrameIndexEntries.
std::vector<FrameIndexEntry> FIEVector;
/// Analysis stats counters
uint64_t NumFunctionsAllClobber{0};
uint64_t CountFunctionsAllClobber{0};
uint64_t NumFunctionsNotOptimized{0};
uint64_t NumFunctionsFailedRestoreFI{0};
uint64_t CountFunctionsNotOptimized{0};
uint64_t CountFunctionsFailedRestoreFI{0};
uint64_t CountDenominator{0};
/// Convenience functions for appending MCAnnotations to instructions with
/// our specific data
void addArgAccessesFor(const BinaryContext &BC, MCInst &Inst,
ArgAccesses &&AA);
void addArgInStackAccessFor(const BinaryContext &BC, MCInst &Inst,
const ArgInStackAccess &Arg);
void addFIEFor(const BinaryContext &BC, MCInst &Inst,
const FrameIndexEntry &FIE);
/// Perform the initial step of populating CallGraphEdges and
/// ReverseCallGraphEdges for all functions in BFs.
void buildCallGraph(BinaryContext &BC,
std::map<uint64_t, BinaryFunction> &BFs);
/// Compute a DFS traversal of the call graph in Functions, CallGraphEdges
/// and ReverseCallGraphEdges and stores it in TopologicalCGOrder.
void buildCGTraversalOrder();
/// Compute the set of registers \p Func may write to during its execution,
/// starting at the point when it is called up until when it returns. Returns
/// a BitVector the size of the target number of registers, representing the
/// set of clobbered registers.
BitVector getFunctionClobberList(const BinaryContext &BC,
const BinaryFunction *Func);
/// Perform the step of building the set of registers clobbered by each
/// function execution, populating RegsKilledMap.
void buildClobberMap(const BinaryContext &BC);
/// Analyzes an instruction and if it is a call, checks the called function
/// to record which args in stack are accessed, if any. Returns true if
/// the args data associated with this instruction were updated.
bool updateArgsTouchedFor(const BinaryContext &BC, const BinaryFunction &BF,
MCInst &Inst, int CurOffset);
/// Performs a pass over \p BF to check for accesses to arguments in stack,
/// flagging those as accessing the caller stack frame. All functions called
/// by \p BF must have been previously analyzed. Returns true if updated
/// args data about this function.
bool computeArgsAccessed(const BinaryContext &BC, BinaryFunction &BF);
/// Alias analysis to disambiguate which frame position is accessed by each
/// instruction in function \p BF. Add MCAnnotation<FrameIndexEntry> to
/// instructions that access a frame position. Return false if it failed
/// to analyze and this information can't be safely determined for \p BF.
bool restoreFrameIndex(const BinaryContext &BC, BinaryFunction &BF);
public:
explicit FrameAnalysis(const cl::opt<bool> &PrintPass)
: BinaryFunctionPass(PrintPass) {
// Position 0 of the vector should be always associated with "assume access
// everything".
ArgAccessesVector.emplace_back(ArgAccesses(/*AssumeEverything*/ true));
}
const char *getName() const override {
return "frame-analysis";
}
/// Return true if we could fully analyze \p Func
bool hasFrameInfo(const BinaryFunction &Func) const {
return AnalyzedFunctions.count(&Func);
}
/// Return true if \p Func cannot operate with a misaligned CFA
bool requiresAlignment(const BinaryFunction &Func) const {
return FunctionsRequireAlignment.count(&Func);
}
/// Compute the set of registers \p Inst may write to, marking them in
/// \p KillSet. If this is a call, try to get the set of registers the call
/// target will write to.
void getInstClobberList(const BinaryContext &BC, const MCInst &Inst,
BitVector &KillSet) const;
/// Functions for retrieving our specific MCAnnotation data from instructions
ErrorOr<ArgAccesses &> getArgAccessesFor(const BinaryContext &BC,
const MCInst &Inst);
ErrorOr<const ArgAccesses &> getArgAccessesFor(const BinaryContext &BC,
const MCInst &Inst) const;
ErrorOr<const FrameIndexEntry &> getFIEFor(const BinaryContext &BC,
const MCInst &Inst) const;
/// Pass entry point
void runOnFunctions(BinaryContext &BC,
std::map<uint64_t, BinaryFunction> &BFs,
std::set<uint64_t> &LargeFunctions) override;
/// Remove all MCAnnotations attached by this pass
void cleanAnnotations(const BinaryContext &BC,
std::map<uint64_t, BinaryFunction> &BFs);
/// Print to standard output statistics about the analysis performed by this
/// pass
void printStats();
};
} // namespace bolt
} // namespace llvm
#endif

View File

@ -523,7 +523,8 @@ class StackPointerTracking : public ForwardDataflow<int> {
if (BC.MII->get(Point.getOpcode())
.hasDefOfPhysReg(Point, MIA->getStackPointer(), *BC.MRI)) {
int64_t Offset = Cur;
if (!MIA->updateStackPointerUpdate(Point, Offset))
if (!MIA->evaluateSimple(Point, Offset, std::make_pair(0, 0),
std::make_pair(0, 0)))
return SUPERPOSITION;
return static_cast<int>(Offset);
@ -606,7 +607,7 @@ bool FrameOptimizerPass::restoreFrameIndex(const BinaryContext &BC,
continue;
}
if (BC.MIA->leaksStackAddress(Inst, *BC.MRI)) {
if (BC.MIA->leaksStackAddress(Inst, *BC.MRI, false)) {
DEBUG(dbgs() << "Leaked stack address, giving up on this function.\n");
DEBUG(dbgs() << "Blame insn: ");
DEBUG(Inst.dump());
@ -614,6 +615,7 @@ bool FrameOptimizerPass::restoreFrameIndex(const BinaryContext &BC,
}
bool IsLoad = false;
bool IsStore = false;
bool IsStoreFromReg = false;
bool IsSimple = false;
int32_t SrcImm{0};
@ -621,8 +623,10 @@ bool FrameOptimizerPass::restoreFrameIndex(const BinaryContext &BC,
MCPhysReg StackPtrReg{0};
int64_t StackOffset{0};
uint8_t Size{0};
if (BC.MIA->isStackAccess(Inst, IsLoad, IsStoreFromReg, Reg, SrcImm,
StackPtrReg, StackOffset, Size, IsSimple)) {
bool IsIndexed = false;
if (BC.MIA->isStackAccess(Inst, IsLoad, IsStore, IsStoreFromReg, Reg,
SrcImm, StackPtrReg, StackOffset, Size,
IsSimple, IsIndexed)) {
assert(Size != 0);
if (CfaRegLocked && CfaRegLockedVal != CfaReg) {
DEBUG(dbgs() << "CFA reg changed, giving up on this function.\n");

View File

@ -0,0 +1,19 @@
//===--- Passes/LivenessAnalysis.cpp --------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//===----------------------------------------------------------------------===//
#include "LivenessAnalysis.h"
namespace llvm {
namespace bolt {
LivenessAnalysis::~LivenessAnalysis() {}
} // end namespace bolt
} // end namespace llvm

View File

@ -0,0 +1,79 @@
//===--- Passes/LivenessAnalysis.h ----------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_LLVM_BOLT_PASSES_LIVENESSANALYSIS_H
#define LLVM_TOOLS_LLVM_BOLT_PASSES_LIVENESSANALYSIS_H
#include "DataflowAnalysis.h"
#include "FrameAnalysis.h"
namespace llvm {
namespace bolt {
class LivenessAnalysis
: public DataflowAnalysis<LivenessAnalysis, BitVector, true> {
friend class DataflowAnalysis<LivenessAnalysis, BitVector, true>;
public:
LivenessAnalysis(const FrameAnalysis &FA, const BinaryContext &BC,
BinaryFunction &BF)
: DataflowAnalysis<LivenessAnalysis, BitVector, true>(BC, BF), FA(FA),
NumRegs(BC.MRI->getNumRegs()) {}
virtual ~LivenessAnalysis();
protected:
/// Reference to the result of stack frame analysis
const FrameAnalysis &FA;
const uint16_t NumRegs;
void preflight() {}
BitVector getStartingStateAtBB(const BinaryBasicBlock &BB) {
return BitVector(NumRegs, false);
}
BitVector getStartingStateAtPoint(const MCInst &Point) {
return BitVector(NumRegs, false);
}
void doConfluence(BitVector &StateOut, const BitVector &StateIn) {
StateOut |= StateIn;
}
BitVector computeNext(const MCInst &Point, const BitVector &Cur) {
BitVector Next = Cur;
// Kill
auto Written = BitVector(NumRegs, false);
if (this->BC.MIA->isCall(Point))
FA.getInstClobberList(this->BC, Point, Written);
else
this->BC.MIA->getWrittenRegs(Point, Written, *this->BC.MRI);
Written.flip();
Next &= Written;
// Gen
if (!this->BC.MIA->isCFI(Point)) {
auto Used = BitVector(NumRegs, false);
this->BC.MIA->getUsedRegs(Point, Used, *this->BC.MRI);
Next |= Used;
}
return Next;
}
StringRef getAnnotationName() const {
return StringRef("LivenessAnalysis");
}
};
} // end namespace bolt
} // end namespace llvm
#endif

View File

@ -0,0 +1,126 @@
//===--- Passes/ReachingDefOrUse.h ----------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_LLVM_BOLT_PASSES_REACHINGDEFORUSE_H
#define LLVM_TOOLS_LLVM_BOLT_PASSES_REACHINGDEFORUSE_H
#include "DataflowAnalysis.h"
namespace llvm {
namespace bolt {
/// If \p Def is true, this computes a forward dataflow equation to
/// propagate reaching definitions.
/// If false, this computes a backward dataflow equation propagating
/// uses to their definitions.
template <bool Def = false>
class ReachingDefOrUse
: public InstrsDataflowAnalysis<ReachingDefOrUse<Def>, !Def> {
friend class DataflowAnalysis<ReachingDefOrUse<Def>, BitVector, !Def>;
public:
ReachingDefOrUse(const FrameAnalysis &FA, const BinaryContext &BC,
BinaryFunction &BF)
: InstrsDataflowAnalysis<ReachingDefOrUse<Def>, !Def>(BC, BF), FA(FA) {}
virtual ~ReachingDefOrUse() {}
bool isReachedBy(MCPhysReg Reg, ExprIterator Candidates) {
for (auto I = Candidates; I != this->expr_end(); ++I) {
auto BV = BitVector(this->BC.MRI->getNumRegs(), false);
if (Def) {
FA.getInstClobberList(this->BC, **I, BV);
} else {
this->BC.MIA->getTouchedRegs(**I, BV, *this->BC.MRI);
}
if (BV[Reg])
return true;
}
return false;
}
bool doesAReachesB(const MCInst &A, const MCInst &B) {
return (*this->getStateAt(B))[this->ExprToIdx[&A]];
}
protected:
/// Reference to the result of stack frame analysis
const FrameAnalysis &FA;
void preflight() {
// Populate our universe of tracked expressions with all instructions
// except pseudos
for (auto &BB : this->Func) {
for (auto &Inst : BB) {
this->Expressions.push_back(&Inst);
this->ExprToIdx[&Inst] = this->NumInstrs++;
}
}
}
BitVector getStartingStateAtBB(const BinaryBasicBlock &BB) {
return BitVector(this->NumInstrs, false);
}
BitVector getStartingStateAtPoint(const MCInst &Point) {
return BitVector(this->NumInstrs, false);
}
void doConfluence(BitVector &StateOut, const BitVector &StateIn) {
StateOut |= StateIn;
}
/// Define the function computing the kill set -- whether expression Y, a
/// tracked expression, will be considered to be dead after executing X.
bool doesXKillsY(const MCInst *X, const MCInst *Y) {
// getClobberedRegs for X and Y. If they intersect, return true
auto XClobbers = BitVector(this->BC.MRI->getNumRegs(), false);
auto YClobbers = BitVector(this->BC.MRI->getNumRegs(), false);
FA.getInstClobberList(this->BC, *X, XClobbers);
// In defs, write after write -> kills first write
// In uses, write after access (read or write) -> kills access
if (Def)
FA.getInstClobberList(this->BC, *Y, YClobbers);
else
this->BC.MIA->getTouchedRegs(*Y, YClobbers, *this->BC.MRI);
// X kills Y if it clobbers Y completely -- this is a conservative approach.
// In practice, we may produce use-def links that may not exist.
XClobbers &= YClobbers;
return XClobbers == YClobbers;
}
BitVector computeNext(const MCInst &Point, const BitVector &Cur) {
BitVector Next = Cur;
// Kill
for (auto I = this->expr_begin(Next), E = this->expr_end(); I != E; ++I) {
assert(*I != nullptr && "Lost pointers");
if (doesXKillsY(&Point, *I)) {
Next.reset(I.getBitVectorIndex());
}
}
// Gen
if (!this->BC.MIA->isCFI(Point)) {
Next.set(this->ExprToIdx[&Point]);
}
return Next;
}
StringRef getAnnotationName() const {
if (Def)
return StringRef("ReachingDefs");
return StringRef("ReachingUses");
}
};
} // end namespace bolt
} // end namespace llvm
#endif

View File

@ -0,0 +1,84 @@
//===--- Passes/ReachingInsns.h -------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_LLVM_BOLT_PASSES_REACHINGINSNS_H
#define LLVM_TOOLS_LLVM_BOLT_PASSES_REACHINGINSNS_H
namespace llvm {
namespace bolt {
template <bool Backward = false>
class ReachingInsns
: public InstrsDataflowAnalysis<ReachingInsns<Backward>, Backward> {
friend class DataflowAnalysis<ReachingInsns<Backward>, BitVector, Backward>;
public:
ReachingInsns(const BinaryContext &BC, BinaryFunction &BF)
: InstrsDataflowAnalysis<ReachingInsns, Backward>(BC, BF) {}
virtual ~ReachingInsns() {}
bool isInLoop(const BinaryBasicBlock &BB) {
const MCInst *First = BB.begin() != BB.end() ? &*BB.begin() : nullptr;
assert(First && "This analysis does not work for empty BB");
return ((*this->getStateAt(BB))[this->ExprToIdx[First]]);
}
bool isInLoop(const MCInst &Inst) {
const BinaryBasicBlock *BB = InsnToBB[&Inst];
assert(BB && "Unknown instruction");
return isInLoop(*BB);
}
protected:
std::unordered_map<const MCInst *, BinaryBasicBlock *> InsnToBB;
void preflight() {
for (auto &BB : this->Func) {
for (auto &Inst : BB) {
this->Expressions.push_back(&Inst);
this->ExprToIdx[&Inst] = this->NumInstrs++;
InsnToBB[&Inst] = &BB;
}
}
}
BitVector getStartingStateAtBB(const BinaryBasicBlock &BB) {
return BitVector(this->NumInstrs, false);
}
BitVector getStartingStateAtPoint(const MCInst &Point) {
return BitVector(this->NumInstrs, false);
}
void doConfluence(BitVector &StateOut, const BitVector &StateIn) {
StateOut |= StateIn;
}
BitVector computeNext(const MCInst &Point, const BitVector &Cur) {
BitVector Next = Cur;
// Gen
if (!this->BC.MIA->isCFI(Point)) {
Next.set(this->ExprToIdx[&Point]);
}
return Next;
}
StringRef getAnnotationName() const {
if (Backward)
return StringRef("ReachingInsnsBackward");
return StringRef("ReachingInsns");
}
};
} // end namespace bolt
} // end namespace llvm
#endif

View File

@ -0,0 +1,28 @@
//===--- Passes/StackPointerTracking.cpp ----------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//===----------------------------------------------------------------------===//
#include "StackPointerTracking.h"
namespace llvm {
namespace bolt {
StackPointerTracking::StackPointerTracking(const BinaryContext &BC,
BinaryFunction &BF)
: StackPointerTrackingBase<StackPointerTracking>(BC, BF) {}
} // end namespace bolt
} // end namespace llvm
llvm::raw_ostream &llvm::operator<<(llvm::raw_ostream &OS,
const std::pair<int, int> &Val) {
OS << Val.first << ", " << Val.second;
return OS;
}

View File

@ -0,0 +1,203 @@
//===--- Passes/StackPointerTracking.h ------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_LLVM_BOLT_PASSES_STACKPOINTERTRACKING_H
#define LLVM_TOOLS_LLVM_BOLT_PASSES_STACKPOINTERTRACKING_H
#include "DataflowAnalysis.h"
namespace llvm {
namespace bolt {
/// Perform a dataflow analysis to track the value of SP as an offset relative
/// to the CFA.
template <typename Derived>
class StackPointerTrackingBase
: public DataflowAnalysis<Derived, std::pair<int, int>> {
friend class DataflowAnalysis<Derived, std::pair<int, int>>;
protected:
void preflight() {}
int getEmpty() { return EMPTY; }
std::pair<int, int> getStartingStateAtBB(const BinaryBasicBlock &BB) {
// Entry BB start with offset 8 from CFA.
// All others start with EMPTY (meaning we don't know anything).
if (BB.isEntryPoint())
return std::make_pair(-8, getEmpty());
return std::make_pair(getEmpty(), getEmpty());
}
std::pair<int, int> getStartingStateAtPoint(const MCInst &Point) {
return std::make_pair(getEmpty(), getEmpty());
}
void doConfluenceSingleReg(int &StateOut, const int &StateIn) {
if (StateOut == EMPTY) {
StateOut = StateIn;
return;
}
if (StateIn == EMPTY || StateIn == StateOut)
return;
// We can't agree on a specific value from this point on
StateOut = SUPERPOSITION;
}
void doConfluence(std::pair<int, int> &StateOut,
const std::pair<int, int> &StateIn) {
doConfluenceSingleReg(StateOut.first, StateIn.first);
doConfluenceSingleReg(StateOut.second, StateIn.second);
}
void doConfluenceWithLP(std::pair<int, int> &StateOut,
const std::pair<int, int> &StateIn,
const MCInst &Invoke) {
int SPVal = StateIn.first;
for (const auto &Operand : Invoke) {
if (Operand.isGnuArgsSize()) {
auto ArgsSize = Operand.getGnuArgsSize();
if (SPVal != EMPTY && SPVal != SUPERPOSITION) {
SPVal += ArgsSize;
}
}
}
doConfluenceSingleReg(StateOut.first, SPVal);
doConfluenceSingleReg(StateOut.second, StateIn.second);
}
int computeNextSP(const MCInst &Point, int SPVal, int FPVal) {
const auto &MIA = this->BC.MIA;
if (int Sz = MIA->getPushSize(Point)) {
if (SPVal == EMPTY || SPVal == SUPERPOSITION)
return SPVal;
return SPVal - Sz;
}
if (int Sz = MIA->getPopSize(Point)) {
if (SPVal == EMPTY || SPVal == SUPERPOSITION)
return SPVal;
return SPVal + Sz;
}
MCPhysReg From, To;
if (MIA->isRegToRegMove(Point, From, To) && To == MIA->getStackPointer() &&
From == MIA->getFramePointer()) {
if (FPVal == EMPTY || FPVal == SUPERPOSITION)
return FPVal;
if (MIA->isLeave(Point))
return FPVal + 8;
else
return FPVal;
}
if (this->BC.MII->get(Point.getOpcode())
.hasDefOfPhysReg(Point, MIA->getStackPointer(), *this->BC.MRI)) {
std::pair<MCPhysReg, int64_t> SP;
if (SPVal != EMPTY && SPVal != SUPERPOSITION)
SP = std::make_pair(MIA->getStackPointer(), SPVal);
else
SP = std::make_pair(0, 0);
std::pair<MCPhysReg, int64_t> FP;
if (FPVal != EMPTY && FPVal != SUPERPOSITION)
FP = std::make_pair(MIA->getFramePointer(), FPVal);
else
FP = std::make_pair(0, 0);
int64_t Output;
if (!MIA->evaluateSimple(Point, Output, SP, FP))
return SUPERPOSITION;
return static_cast<int>(Output);
}
return SPVal;
}
int computeNextFP(const MCInst &Point, int SPVal, int FPVal) {
const auto &MIA = this->BC.MIA;
MCPhysReg From, To;
if (MIA->isRegToRegMove(Point, From, To) && To == MIA->getFramePointer() &&
From == MIA->getStackPointer()) {
HasFramePointer = true;
return SPVal;
}
if (this->BC.MII->get(Point.getOpcode())
.hasDefOfPhysReg(Point, MIA->getFramePointer(), *this->BC.MRI)) {
std::pair<MCPhysReg, int64_t> FP;
if (FPVal != EMPTY && FPVal != SUPERPOSITION)
FP = std::make_pair(MIA->getFramePointer(), FPVal);
else
FP = std::make_pair(0, 0);
std::pair<MCPhysReg, int64_t> SP;
if (SPVal != EMPTY && SPVal != SUPERPOSITION)
SP = std::make_pair(MIA->getStackPointer(), SPVal);
else
SP = std::make_pair(0, 0);
int64_t Output;
if (!MIA->evaluateSimple(Point, Output, SP, FP))
return SUPERPOSITION;
if (!HasFramePointer) {
if (MIA->leaksStackAddress(Point, *this->BC.MRI, false)) {
HasFramePointer = true;
}
}
return static_cast<int>(Output);
}
return FPVal;
}
std::pair<int, int> computeNext(const MCInst &Point,
const std::pair<int, int> &Cur) {
return std::make_pair(computeNextSP(Point, Cur.first, Cur.second),
computeNextFP(Point, Cur.first, Cur.second));
}
StringRef getAnnotationName() const {
return StringRef("StackPointerTracking");
}
public:
StackPointerTrackingBase(const BinaryContext &BC, BinaryFunction &BF)
: DataflowAnalysis<Derived, std::pair<int, int>>(BC, BF) {}
virtual ~StackPointerTrackingBase() {}
bool HasFramePointer{false};
static constexpr int SUPERPOSITION = std::numeric_limits<int>::max();
static constexpr int EMPTY = std::numeric_limits<int>::min();
};
class StackPointerTracking
: public StackPointerTrackingBase<StackPointerTracking> {
friend class DataflowAnalysis<StackPointerTracking, std::pair<int, int>>;
public:
StackPointerTracking(const BinaryContext &BC, BinaryFunction &BF);
virtual ~StackPointerTracking() {}
};
} // end namespace bolt
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
const std::pair<int, int> &Val);
} // end namespace llvm
#endif