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;
|
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.
|
/// Return the name of the function as extracted from the binary file.
|
||||||
/// If the function has multiple names - return the last one
|
/// If the function has multiple names - return the last one
|
||||||
/// followed by "(*#<numnames>)".
|
/// followed by "(*#<numnames>)".
|
||||||
|
@ -1412,6 +1426,14 @@ public:
|
||||||
return;
|
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
|
/// Insert a CFI pseudo instruction in a basic block. This pseudo instruction
|
||||||
/// is a placeholder that refers to a real MCCFIInstruction object kept by
|
/// is a placeholder that refers to a real MCCFIInstruction object kept by
|
||||||
/// this function that will be emitted at that position.
|
/// this function that will be emitted at that position.
|
||||||
|
@ -1424,6 +1446,14 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve the MCCFIInstruction object associated with a CFI pseudo.
|
/// 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 {
|
const MCCFIInstruction* getCFIFor(const MCInst &Instr) const {
|
||||||
if (!BC.MIA->isCFI(Instr))
|
if (!BC.MIA->isCFI(Instr))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
add_llvm_library(LLVMBOLTPasses
|
add_llvm_library(LLVMBOLTPasses
|
||||||
BinaryPasses.cpp
|
BinaryPasses.cpp
|
||||||
|
DataflowAnalysis.cpp
|
||||||
|
DataflowInfoManager.cpp
|
||||||
|
FrameAnalysis.cpp
|
||||||
FrameOptimizer.cpp
|
FrameOptimizer.cpp
|
||||||
HFSort.cpp
|
HFSort.cpp
|
||||||
HFSortPlus.cpp
|
HFSortPlus.cpp
|
||||||
IndirectCallPromotion.cpp
|
IndirectCallPromotion.cpp
|
||||||
Inliner.cpp
|
Inliner.cpp
|
||||||
|
LivenessAnalysis.cpp
|
||||||
ReorderAlgorithm.cpp
|
ReorderAlgorithm.cpp
|
||||||
|
StackPointerTracking.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
include_directories( ${LLVM_MAIN_SRC_DIR}/tools/llvm-bolt )
|
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())
|
if (BC.MII->get(Point.getOpcode())
|
||||||
.hasDefOfPhysReg(Point, MIA->getStackPointer(), *BC.MRI)) {
|
.hasDefOfPhysReg(Point, MIA->getStackPointer(), *BC.MRI)) {
|
||||||
int64_t Offset = Cur;
|
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 SUPERPOSITION;
|
||||||
|
|
||||||
return static_cast<int>(Offset);
|
return static_cast<int>(Offset);
|
||||||
|
@ -606,7 +607,7 @@ bool FrameOptimizerPass::restoreFrameIndex(const BinaryContext &BC,
|
||||||
continue;
|
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() << "Leaked stack address, giving up on this function.\n");
|
||||||
DEBUG(dbgs() << "Blame insn: ");
|
DEBUG(dbgs() << "Blame insn: ");
|
||||||
DEBUG(Inst.dump());
|
DEBUG(Inst.dump());
|
||||||
|
@ -614,6 +615,7 @@ bool FrameOptimizerPass::restoreFrameIndex(const BinaryContext &BC,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsLoad = false;
|
bool IsLoad = false;
|
||||||
|
bool IsStore = false;
|
||||||
bool IsStoreFromReg = false;
|
bool IsStoreFromReg = false;
|
||||||
bool IsSimple = false;
|
bool IsSimple = false;
|
||||||
int32_t SrcImm{0};
|
int32_t SrcImm{0};
|
||||||
|
@ -621,8 +623,10 @@ bool FrameOptimizerPass::restoreFrameIndex(const BinaryContext &BC,
|
||||||
MCPhysReg StackPtrReg{0};
|
MCPhysReg StackPtrReg{0};
|
||||||
int64_t StackOffset{0};
|
int64_t StackOffset{0};
|
||||||
uint8_t Size{0};
|
uint8_t Size{0};
|
||||||
if (BC.MIA->isStackAccess(Inst, IsLoad, IsStoreFromReg, Reg, SrcImm,
|
bool IsIndexed = false;
|
||||||
StackPtrReg, StackOffset, Size, IsSimple)) {
|
if (BC.MIA->isStackAccess(Inst, IsLoad, IsStore, IsStoreFromReg, Reg,
|
||||||
|
SrcImm, StackPtrReg, StackOffset, Size,
|
||||||
|
IsSimple, IsIndexed)) {
|
||||||
assert(Size != 0);
|
assert(Size != 0);
|
||||||
if (CfaRegLocked && CfaRegLockedVal != CfaReg) {
|
if (CfaRegLocked && CfaRegLockedVal != CfaReg) {
|
||||||
DEBUG(dbgs() << "CFA reg changed, giving up on this function.\n");
|
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