diff --git a/bolt/BinaryFunction.h b/bolt/BinaryFunction.h index 05d7d273459e..15a48d3ee0bc 100644 --- a/bolt/BinaryFunction.h +++ b/bolt/BinaryFunction.h @@ -974,6 +974,20 @@ public: return nullptr; } + /// Retrieve the landing pad BB associated with invoke instruction \p Invoke + /// that is in \p BB. Return nullptr if none exists + BinaryBasicBlock *getLandingPadBBFor(const BinaryBasicBlock &BB, + const MCInst &InvokeInst) { + assert(BC.MIA->isInvoke(InvokeInst) && "must be invoke instruction"); + MCLandingPad LP = BC.MIA->getEHInfo(InvokeInst); + if (LP.first) { + auto *LBB = BB.getLandingPad(LP.first); + assert (LBB && "Landing pad should be defined"); + return LBB; + } + return nullptr; + } + /// Return the name of the function as extracted from the binary file. /// If the function has multiple names - return the last one /// followed by "(*#)". @@ -1412,6 +1426,14 @@ public: return; } + BinaryBasicBlock::iterator addCFIInstruction(BinaryBasicBlock *BB, + BinaryBasicBlock::iterator Pos, + MCCFIInstruction &&Inst) { + auto Idx = FrameInstructions.size(); + FrameInstructions.emplace_back(std::forward(Inst)); + return addCFIPseudo(BB, Pos, Idx); + } + /// Insert a CFI pseudo instruction in a basic block. This pseudo instruction /// is a placeholder that refers to a real MCCFIInstruction object kept by /// this function that will be emitted at that position. @@ -1424,6 +1446,14 @@ public: } /// Retrieve the MCCFIInstruction object associated with a CFI pseudo. + MCCFIInstruction* getCFIFor(const MCInst &Instr) { + if (!BC.MIA->isCFI(Instr)) + return nullptr; + uint32_t Offset = Instr.getOperand(0).getImm(); + assert(Offset < FrameInstructions.size() && "Invalid CFI offset"); + return &FrameInstructions[Offset]; + } + const MCCFIInstruction* getCFIFor(const MCInst &Instr) const { if (!BC.MIA->isCFI(Instr)) return nullptr; diff --git a/bolt/Passes/CMakeLists.txt b/bolt/Passes/CMakeLists.txt index 61ce50fefbb8..b764de69c4c5 100644 --- a/bolt/Passes/CMakeLists.txt +++ b/bolt/Passes/CMakeLists.txt @@ -1,11 +1,16 @@ add_llvm_library(LLVMBOLTPasses BinaryPasses.cpp + DataflowAnalysis.cpp + DataflowInfoManager.cpp + FrameAnalysis.cpp FrameOptimizer.cpp HFSort.cpp HFSortPlus.cpp IndirectCallPromotion.cpp Inliner.cpp + LivenessAnalysis.cpp ReorderAlgorithm.cpp + StackPointerTracking.cpp ) include_directories( ${LLVM_MAIN_SRC_DIR}/tools/llvm-bolt ) diff --git a/bolt/Passes/DataflowAnalysis.cpp b/bolt/Passes/DataflowAnalysis.cpp new file mode 100644 index 000000000000..e3a1894a930b --- /dev/null +++ b/bolt/Passes/DataflowAnalysis.cpp @@ -0,0 +1,40 @@ +#include "DataflowAnalysis.h" + +namespace llvm { +namespace bolt { + +void doForAllPreds(const BinaryContext &BC, const BinaryBasicBlock &BB, + std::function 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 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; +} diff --git a/bolt/Passes/DataflowAnalysis.h b/bolt/Passes/DataflowAnalysis.h new file mode 100644 index 000000000000..1252be07eaa2 --- /dev/null +++ b/bolt/Passes/DataflowAnalysis.h @@ -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 + +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; +}; + +/// 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 Task); + +/// Operates on all successors of a basic block. +void doForAllSuccs(const BinaryBasicBlock &BB, + std::function 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 +class DataflowAnalysis { + /// CRTP convenience methods + Derived &derived() { + return *static_cast(this); + } + + const Derived &const_derived() const { + return *static_cast(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 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 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( + 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 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 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 getStateAt(const MCInst &Point) const { + return BC.MIA->tryGetAnnotationAs( + 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 getStateAt(ProgramPoint Point) const { + if (Point.isBB()) + return getStateAt(*Point.getBB()); + return getStateAt(*Point.getInst()); + } + + ErrorOr 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). + ErrorOrgetStateBefore(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 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 { + const BitVector *BV; + const std::vector &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 &Exprs) + : BV(BV), Expressions(Exprs) { + Idx = BV->find_first(); + } + ExprIterator(const BitVector *BV, const std::vector &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 +class InstrsDataflowAnalysis + : public DataflowAnalysis { +public: + /// These iterator functions offer access to the set of pointers to + /// instructions in a given program point + template + 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 Expressions; + /// Maps expressions defs (MCInsts) to its index in the Expressions vector + std::unordered_map ExprToIdx; + + InstrsDataflowAnalysis(const BinaryContext &BC, BinaryFunction &BF) + : DataflowAnalysis(BC, BF) {} + virtual ~InstrsDataflowAnalysis() {} +}; + +} // namespace bolt + +/// DenseMapInfo allows us to use the DenseMap LLVM data structure to store +/// ProgramPoints. +template<> struct DenseMapInfo { + static inline bolt::ProgramPoint getEmptyKey() { + uintptr_t Val = static_cast(-1); + Val <<= PointerLikeTypeTraits::NumLowBitsAvailable; + return bolt::ProgramPoint(reinterpret_cast(Val)); + } + static inline bolt::ProgramPoint getTombstoneKey() { + uintptr_t Val = static_cast(-2); + Val <<= PointerLikeTypeTraits::NumLowBitsAvailable; + return bolt::ProgramPoint(reinterpret_cast(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 diff --git a/bolt/Passes/DataflowInfoManager.cpp b/bolt/Passes/DataflowInfoManager.cpp new file mode 100644 index 000000000000..0c4cdbe99e06 --- /dev/null +++ b/bolt/Passes/DataflowInfoManager.cpp @@ -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 &DataflowInfoManager::getReachingDefs() { + if (RD) + return *RD; + assert(FA && "FrameAnalysis required"); + RD.reset(new ReachingDefOrUse(*FA, BC, BF)); + { + NamedRegionTimer T1("RD", "Dataflow", true); + RD->run(); + } + return *RD; +} + +void DataflowInfoManager::invalidateReachingDefs() { + RD.reset(nullptr); +} + +ReachingDefOrUse &DataflowInfoManager::getReachingUses() { + if (RU) + return *RU; + assert(FA && "FrameAnalysis required"); + RU.reset(new ReachingDefOrUse(*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 &DataflowInfoManager::getDominatorAnalysis() { + if (DA) + return *DA; + DA.reset(new DominatorAnalysis(BC, BF)); + { + NamedRegionTimer T1("DA", "Dataflow", true); + DA->run(); + } + return *DA; +} + +void DataflowInfoManager::invalidateDominatorAnalysis() { + DA.reset(nullptr); +} + +DominatorAnalysis &DataflowInfoManager::getPostDominatorAnalysis() { + if (PDA) + return *PDA; + PDA.reset(new DominatorAnalysis(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 &DataflowInfoManager::getReachingInsns() { + if (RI) + return *RI; + RI.reset(new ReachingInsns(BC, BF)); + { + NamedRegionTimer T1("RI", "Dataflow", true); + RI->run(); + } + return *RI; +} + +void DataflowInfoManager::invalidateReachingInsns() { + RI.reset(nullptr); +} + +ReachingInsns &DataflowInfoManager::getReachingInsnsBackwards() { + if (RIB) + return *RIB; + RIB.reset(new ReachingInsns(BC, BF)); + { + NamedRegionTimer T1("RIB", "Dataflow", true); + RIB->run(); + } + return *RIB; +} + +void DataflowInfoManager::invalidateReachingInsnsBackwards() { + RIB.reset(nullptr); +} + +std::unordered_map & +DataflowInfoManager::getInsnToBBMap() { + if (InsnToBB) + return *InsnToBB; + InsnToBB.reset(new std::unordered_map()); + 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 diff --git a/bolt/Passes/DataflowInfoManager.h b/bolt/Passes/DataflowInfoManager.h new file mode 100644 index 000000000000..a9ef9f7d897d --- /dev/null +++ b/bolt/Passes/DataflowInfoManager.h @@ -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> RD; + std::unique_ptr> RU; + std::unique_ptr LA; + std::unique_ptr> DA; + std::unique_ptr> PDA; + std::unique_ptr SPT; + std::unique_ptr> RI; + std::unique_ptr> RIB; + std::unique_ptr> + InsnToBB; + +public: + DataflowInfoManager(const FrameAnalysis *FA, const BinaryContext &BC, + BinaryFunction &BF) : FA(FA), BC(BC), BF(BF) {}; + + ReachingDefOrUse &getReachingDefs(); + void invalidateReachingDefs(); + ReachingDefOrUse &getReachingUses(); + void invalidateReachingUses(); + LivenessAnalysis &getLivenessAnalysis(); + void invalidateLivenessAnalysis(); + DominatorAnalysis &getDominatorAnalysis(); + void invalidateDominatorAnalysis(); + DominatorAnalysis &getPostDominatorAnalysis(); + void invalidatePostDominatorAnalysis(); + StackPointerTracking &getStackPointerTracking(); + void invalidateStackPointerTracking(); + ReachingInsns &getReachingInsns(); + void invalidateReachingInsns(); + ReachingInsns &getReachingInsnsBackwards(); + void invalidateReachingInsnsBackwards(); + std::unordered_map &getInsnToBBMap(); + void invalidateInsnToBBMap(); + void invalidateAll(); +}; + +} // end namespace bolt +} // end namespace llvm + +#endif diff --git a/bolt/Passes/DominatorAnalysis.h b/bolt/Passes/DominatorAnalysis.h new file mode 100644 index 000000000000..87eef5f7662f --- /dev/null +++ b/bolt/Passes/DominatorAnalysis.h @@ -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 +class DominatorAnalysis + : public InstrsDataflowAnalysis, Backward> { + friend class DataflowAnalysis, BitVector, + Backward>; + +public: + DominatorAnalysis(const BinaryContext &BC, BinaryFunction &BF) + : InstrsDataflowAnalysis, Backward>(BC, BF) {} + virtual ~DominatorAnalysis() {} + + SmallVector getDominanceFrontierFor(const MCInst &Dom) { + SmallVector 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 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 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 diff --git a/bolt/Passes/FrameAnalysis.cpp b/bolt/Passes/FrameAnalysis.cpp new file mode 100644 index 000000000000..a5aadf9522f9 --- /dev/null +++ b/bolt/Passes/FrameAnalysis.cpp @@ -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 + +#define DEBUG_TYPE "fa" + +using namespace llvm; + +namespace opts { +extern cl::opt Verbosity; +extern bool shouldProcess(const bolt::BinaryFunction &Function); + +static cl::list + FrameOptFunctionNames("funcs-fop", cl::CommaSeparated, + cl::desc("list of functions to apply frame opts"), + cl::value_desc("func1,func2,func3,...")); + +static cl::opt 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"; + 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> 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 +FrameAnalysis::getArgAccessesFor(const BinaryContext &BC, const MCInst &Inst) { + if (auto Idx = BC.MIA->tryGetAnnotationAs(Inst, "ArgAccessEntry")) { + assert(ArgAccessesVector.size() > *Idx && "Out of bounds"); + return ArgAccessesVector[*Idx]; + } + return make_error_code(errc::result_out_of_range); +} + +ErrorOr +FrameAnalysis::getArgAccessesFor(const BinaryContext &BC, + const MCInst &Inst) const { + if (auto Idx = BC.MIA->tryGetAnnotationAs(Inst, "ArgAccessEntry")) { + assert(ArgAccessesVector.size() > *Idx && "Out of bounds"); + return ArgAccessesVector[*Idx]; + } + return make_error_code(errc::result_out_of_range); +} + +ErrorOr +FrameAnalysis::getFIEFor(const BinaryContext &BC, const MCInst &Inst) const { + if (auto Idx = + BC.MIA->tryGetAnnotationAs(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 &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 NodeStatus; + std::stack 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 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 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 &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 &BFs, + std::set &) { + { + 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 diff --git a/bolt/Passes/FrameAnalysis.h b/bolt/Passes/FrameAnalysis.h new file mode 100644 index 000000000000..c2c2938d60b2 --- /dev/null +++ b/bolt/Passes/FrameAnalysis.h @@ -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 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 Functions; + /// Model the "function calls function" edges + std::map> + CallGraphEdges; + /// Model the "function called by function" edges + std::map> + ReverseCallGraphEdges; + /// DFS or reverse post-ordering of the call graph nodes to allow us to + /// traverse the call graph bottom-up + std::deque 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 RegsKilledMap; + + /// Map functions to the set of tuples representing + /// accesses to stack positions that belongs to caller + std::map>> + ArgsTouchedMap; + + /// The set of functions we were able to perform the full analysis up to + /// restoring frame indexes for all load/store instructions. + DenseSet AnalyzedFunctions; + + /// Set of functions that require the stack to be 16B aligned + DenseSet FunctionsRequireAlignment; + + /// Owns ArgAccesses for all instructions. References to elements are + /// attached to instructions as indexes to this vector, in MCAnnotations. + std::vector ArgAccessesVector; + /// Same for FrameIndexEntries. + std::vector 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 &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 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 &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 getArgAccessesFor(const BinaryContext &BC, + const MCInst &Inst); + + ErrorOr getArgAccessesFor(const BinaryContext &BC, + const MCInst &Inst) const; + + ErrorOr getFIEFor(const BinaryContext &BC, + const MCInst &Inst) const; + + /// Pass entry point + void runOnFunctions(BinaryContext &BC, + std::map &BFs, + std::set &LargeFunctions) override; + + /// Remove all MCAnnotations attached by this pass + void cleanAnnotations(const BinaryContext &BC, + std::map &BFs); + + + /// Print to standard output statistics about the analysis performed by this + /// pass + void printStats(); +}; + +} // namespace bolt +} // namespace llvm + + +#endif diff --git a/bolt/Passes/FrameOptimizer.cpp b/bolt/Passes/FrameOptimizer.cpp index 7e87b15f22d0..2eb3fafc38eb 100644 --- a/bolt/Passes/FrameOptimizer.cpp +++ b/bolt/Passes/FrameOptimizer.cpp @@ -523,7 +523,8 @@ class StackPointerTracking : public ForwardDataflow { if (BC.MII->get(Point.getOpcode()) .hasDefOfPhysReg(Point, MIA->getStackPointer(), *BC.MRI)) { int64_t Offset = Cur; - if (!MIA->updateStackPointerUpdate(Point, Offset)) + if (!MIA->evaluateSimple(Point, Offset, std::make_pair(0, 0), + std::make_pair(0, 0))) return SUPERPOSITION; return static_cast(Offset); @@ -606,7 +607,7 @@ bool FrameOptimizerPass::restoreFrameIndex(const BinaryContext &BC, continue; } - if (BC.MIA->leaksStackAddress(Inst, *BC.MRI)) { + if (BC.MIA->leaksStackAddress(Inst, *BC.MRI, false)) { DEBUG(dbgs() << "Leaked stack address, giving up on this function.\n"); DEBUG(dbgs() << "Blame insn: "); DEBUG(Inst.dump()); @@ -614,6 +615,7 @@ bool FrameOptimizerPass::restoreFrameIndex(const BinaryContext &BC, } bool IsLoad = false; + bool IsStore = false; bool IsStoreFromReg = false; bool IsSimple = false; int32_t SrcImm{0}; @@ -621,8 +623,10 @@ bool FrameOptimizerPass::restoreFrameIndex(const BinaryContext &BC, MCPhysReg StackPtrReg{0}; int64_t StackOffset{0}; uint8_t Size{0}; - if (BC.MIA->isStackAccess(Inst, IsLoad, IsStoreFromReg, Reg, SrcImm, - StackPtrReg, StackOffset, Size, IsSimple)) { + bool IsIndexed = false; + if (BC.MIA->isStackAccess(Inst, IsLoad, IsStore, IsStoreFromReg, Reg, + SrcImm, StackPtrReg, StackOffset, Size, + IsSimple, IsIndexed)) { assert(Size != 0); if (CfaRegLocked && CfaRegLockedVal != CfaReg) { DEBUG(dbgs() << "CFA reg changed, giving up on this function.\n"); diff --git a/bolt/Passes/LivenessAnalysis.cpp b/bolt/Passes/LivenessAnalysis.cpp new file mode 100644 index 000000000000..db8156cc1ed4 --- /dev/null +++ b/bolt/Passes/LivenessAnalysis.cpp @@ -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 diff --git a/bolt/Passes/LivenessAnalysis.h b/bolt/Passes/LivenessAnalysis.h new file mode 100644 index 000000000000..f95a9ef12503 --- /dev/null +++ b/bolt/Passes/LivenessAnalysis.h @@ -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 { + friend class DataflowAnalysis; + +public: + LivenessAnalysis(const FrameAnalysis &FA, const BinaryContext &BC, + BinaryFunction &BF) + : DataflowAnalysis(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 diff --git a/bolt/Passes/ReachingDefOrUse.h b/bolt/Passes/ReachingDefOrUse.h new file mode 100644 index 000000000000..ca67389b281a --- /dev/null +++ b/bolt/Passes/ReachingDefOrUse.h @@ -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 +class ReachingDefOrUse + : public InstrsDataflowAnalysis, !Def> { + friend class DataflowAnalysis, BitVector, !Def>; + +public: + ReachingDefOrUse(const FrameAnalysis &FA, const BinaryContext &BC, + BinaryFunction &BF) + : InstrsDataflowAnalysis, !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 diff --git a/bolt/Passes/ReachingInsns.h b/bolt/Passes/ReachingInsns.h new file mode 100644 index 000000000000..4bcdb3d843dd --- /dev/null +++ b/bolt/Passes/ReachingInsns.h @@ -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 +class ReachingInsns + : public InstrsDataflowAnalysis, Backward> { + friend class DataflowAnalysis, BitVector, Backward>; + +public: + ReachingInsns(const BinaryContext &BC, BinaryFunction &BF) + : InstrsDataflowAnalysis(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 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 diff --git a/bolt/Passes/StackPointerTracking.cpp b/bolt/Passes/StackPointerTracking.cpp new file mode 100644 index 000000000000..ce12627242cb --- /dev/null +++ b/bolt/Passes/StackPointerTracking.cpp @@ -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(BC, BF) {} + +} // end namespace bolt +} // end namespace llvm + +llvm::raw_ostream &llvm::operator<<(llvm::raw_ostream &OS, + const std::pair &Val) { + OS << Val.first << ", " << Val.second; + return OS; +} diff --git a/bolt/Passes/StackPointerTracking.h b/bolt/Passes/StackPointerTracking.h new file mode 100644 index 000000000000..7f02e766dfc9 --- /dev/null +++ b/bolt/Passes/StackPointerTracking.h @@ -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 +class StackPointerTrackingBase + : public DataflowAnalysis> { + friend class DataflowAnalysis>; + +protected: + void preflight() {} + + int getEmpty() { return EMPTY; } + + std::pair 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 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 &StateOut, + const std::pair &StateIn) { + doConfluenceSingleReg(StateOut.first, StateIn.first); + doConfluenceSingleReg(StateOut.second, StateIn.second); + } + + void doConfluenceWithLP(std::pair &StateOut, + const std::pair &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 SP; + if (SPVal != EMPTY && SPVal != SUPERPOSITION) + SP = std::make_pair(MIA->getStackPointer(), SPVal); + else + SP = std::make_pair(0, 0); + std::pair 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(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 FP; + if (FPVal != EMPTY && FPVal != SUPERPOSITION) + FP = std::make_pair(MIA->getFramePointer(), FPVal); + else + FP = std::make_pair(0, 0); + std::pair 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(Output); + } + + return FPVal; + } + + std::pair computeNext(const MCInst &Point, + const std::pair &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>(BC, BF) {} + virtual ~StackPointerTrackingBase() {} + bool HasFramePointer{false}; + + static constexpr int SUPERPOSITION = std::numeric_limits::max(); + static constexpr int EMPTY = std::numeric_limits::min(); +}; + +class StackPointerTracking + : public StackPointerTrackingBase { + friend class DataflowAnalysis>; + +public: + StackPointerTracking(const BinaryContext &BC, BinaryFunction &BF); + virtual ~StackPointerTracking() {} +}; + +} // end namespace bolt + +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const std::pair &Val); + +} // end namespace llvm + + +#endif