forked from OSchip/llvm-project
[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:
parent
457b7f14b9
commit
511a1c78b2
|
@ -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;
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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");
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue