#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "revng/ADT/Queue.h" #include "ASSlot.h" #include "FunctionABI.h" namespace StackAnalysis { /// \brief Instruction of the ABI IR class ABIIRInstruction { public: enum Opcode { Load, Store, DirectCall, IndirectCall }; private: /// Instruction opcode Opcode O; /// Load/store target address const ASSlot Target; // // Call-only fields // /// Reference to the function call FunctionCall Call; /// Result of the ABI analysis for the callee /// /// \note FunctionABI can be quite large, create an instance only if needed. std::unique_ptr ABI; /// Set of caller stack slots written by the callee std::set WrittenStackSlots; private: ABIIRInstruction(Opcode O, FunctionCall Call, FunctionABI ABI, std::set WrittenStackSlots) : O(O), Target(ASSlot::invalid()), Call(Call), ABI(new FunctionABI(std::move(ABI))), WrittenStackSlots(std::move(WrittenStackSlots)) { revng_assert(O == DirectCall); } ABIIRInstruction(Opcode O, ASSlot Target) : O(O), Target(Target), ABI(), WrittenStackSlots() { revng_assert(O == Load || O == Store); } ABIIRInstruction(Opcode O, FunctionCall Call) : O(O), Target(ASSlot::invalid()), Call(Call), ABI(), WrittenStackSlots() { revng_assert(O == IndirectCall); } public: static ABIIRInstruction createLoad(const ASSlot Target) { return ABIIRInstruction(Load, Target); } static ABIIRInstruction createStore(const ASSlot Target) { return ABIIRInstruction(Store, Target); } static ABIIRInstruction createDirectCall(FunctionCall Call, FunctionABI ABI, std::set WrittenStackSlots) { return ABIIRInstruction(DirectCall, Call, std::move(ABI), std::move(WrittenStackSlots)); } static ABIIRInstruction createIndirectCall(FunctionCall Call) { return ABIIRInstruction(IndirectCall, Call); } public: Opcode opcode() const { return O; } bool isCall() const { return O == DirectCall or O == IndirectCall; } bool isStore() const { return O == Store; } const ASSlot target() const { revng_assert(O == Load || O == Store); return Target; } const FunctionABI &abi() const { revng_assert(O == DirectCall); return *ABI; } const std::set &stackArguments() const { revng_assert(O == DirectCall); return WrittenStackSlots; } FunctionCall call() const { revng_assert(isCall()); revng_assert(Call.callInstruction() != nullptr); return Call; } void dump(const llvm::Module *M) const debug_function { dump(dbg, M); } template void dump(T &Output, const llvm::Module *M) const { switch (O) { case Load: Output << "Load from "; target().dump(M, Output); break; case Store: Output << "Store to "; target().dump(M, Output); break; case DirectCall: Output << "DirectCall to " << getName(call().callee()); Output << " from " << getName(call().callInstruction()); break; case IndirectCall: Output << "IndirectCall from " << getName(call().callInstruction()); break; } } }; /// \brief Basic block of the ABI IR, a container of ABIIRInstructions class ABIIRBasicBlock { // The ABIFunction class is our friend so it can finalize us friend class ABIFunction; public: using links_container = llvm::SmallVector; using links_iterator = typename links_container::iterator; using links_const_iterator = typename links_container::const_iterator; using links_range = llvm::iterator_range; using links_const_range = llvm::iterator_range; using container = std::vector; using iterator = typename container::iterator; using const_iterator = typename container::const_iterator; using reverse_iterator = typename container::reverse_iterator; using const_reverse_iterator = typename container::const_reverse_iterator; using range = llvm::iterator_range; using const_range = llvm::iterator_range; using reverse_range = llvm::iterator_range; using const_reverse_range = llvm::iterator_range; private: /// The instructions contained in this basic block std::vector Instructions; /// List of successors links_container Successors; /// List of predecessors /// /// \note This field is initialized only after ABIFunction::finalize is called links_container Predecessors; /// Reference to the corresponding basic block llvm::BasicBlock *BB; /// Flag to identify return basic blocks bool IsReturn; public: ABIIRBasicBlock(llvm::BasicBlock *BB) : BB(BB), IsReturn(false) {} public: /// \brief Purge basic block content void clear() { revng_assert(Predecessors.empty()); Instructions.clear(); Successors.clear(); IsReturn = false; } bool isPartOfFinalResults() const { return IsReturn; } void setReturn() { IsReturn = true; } void append(ABIIRInstruction I) { Instructions.push_back(std::move(I)); } void addSuccessor(ABIIRBasicBlock *Successor) { Successors.push_back(Successor); } size_t successor_size() const { return Successors.size(); } links_const_iterator successor_begin() const { return Successors.begin(); } links_const_iterator successor_end() const { return Successors.end(); } links_const_range successors() const { return llvm::make_range(Successors.begin(), Successors.end()); } links_iterator successor_begin() { return Successors.begin(); } links_iterator successor_end() { return Successors.end(); } links_range successors() { return llvm::make_range(Successors.begin(), Successors.end()); } size_t predecessor_size() const { return Predecessors.size(); } links_const_iterator predecessor_begin() const { return Predecessors.begin(); } links_const_iterator predecessor_end() const { return Predecessors.end(); } links_const_range predecessors() const { return llvm::make_range(Predecessors.begin(), Predecessors.end()); } links_iterator predecessor_begin() { return Predecessors.begin(); } links_iterator predecessor_end() { return Predecessors.end(); } links_range predecessors() { return llvm::make_range(Predecessors.begin(), Predecessors.end()); } template size_t next_size() const { return Forward ? successor_size() : predecessor_size(); } template links_const_range next() const { return Forward ? successors() : predecessors(); } size_t size() const { return Instructions.size(); } iterator begin() { return Instructions.begin(); } iterator end() { return Instructions.end(); } const_iterator begin() const { return Instructions.begin(); } const_iterator end() const { return Instructions.end(); } reverse_iterator rbegin() { return Instructions.rbegin(); } reverse_iterator rend() { return Instructions.rend(); } const_reverse_iterator rbegin() const { return Instructions.rbegin(); } const_reverse_iterator rend() const { return Instructions.rend(); } llvm::BasicBlock *basicBlock() const { return BB; } void dump(const llvm::Module *M, const char *Prefix = "") const debug_function { dump(dbg, M, Prefix); } template void dump(T &Output, const llvm::Module *M, const char *Prefix = "") const { Output << Prefix << "From basic block " << ::getName(BB); if (IsReturn) Output << " [IsReturn]"; Output << "\n"; if (not Predecessors.empty()) { Output << Prefix << "Predecessors:\n"; for (const ABIIRBasicBlock *Predecessor : Predecessors) { Output << Prefix << " " << ::getName(Predecessor->basicBlock()) << "\n"; } Output << Prefix << "\n"; } Output << Prefix << "Instructions:\n"; for (const ABIIRInstruction &I : Instructions) { Output << Prefix << " "; I.dump(Output, M); Output << "\n"; } Output << "\n"; if (not Successors.empty()) { Output << Prefix << "Successors:\n"; for (const ABIIRBasicBlock *Successor : Successors) Output << Prefix << " " << ::getName(Successor->basicBlock()) << "\n"; Output << Prefix << "\n"; } } }; /// \brief The ABI IR, a container of ABIIRBasicBlocks class ABIFunction { public: template using VectorOfPairs = std::vector>; using calls_container = VectorOfPairs; using calls_iterator = calls_container::iterator; using calls_range = llvm::iterator_range; using calls_const_iterator = calls_container::const_iterator; using calls_const_range = llvm::iterator_range; using returns_container = std::vector; using returns_iterator = returns_container::iterator; using returns_range = llvm::iterator_range; using returns_const_iterator = returns_container::const_iterator; using returns_const_range = llvm::iterator_range; private: /// Storage for ABI IR basic blocks, associated to their original counterpart /// /// \note Don't move after Entry std::map BBMap; /// Pointer to the entry basic block of this function llvm::BasicBlock *Entry; ABIIRBasicBlock *IREntry; /// Vector of all the function calls in this function calls_container Calls; /// Vector of all the return basic blocks returns_container FinalBBs; public: ABIFunction(llvm::BasicBlock *Entry) : Entry(Entry), IREntry(&BBMap.emplace(Entry, ABIIRBasicBlock(Entry)).first->second) {} ABIIRBasicBlock *entry() const { return IREntry; } size_t size() const { return BBMap.size(); } /// \brief Purge all the data in this IR void reset() { BBMap.clear(); Calls.clear(); FinalBBs.clear(); IREntry = &BBMap.emplace(Entry, ABIIRBasicBlock(Entry)).first->second; } /// \brief Finalize the IR after initially populating it /// /// This method basically populates the backward links of the CFG, identifies /// all the function calls and ensures the entry basic block has no inbound /// edges. void finalize(); /// \brief Identify calls leading to contradition std::set incoherentCalls(); std::set writtenRegisters() const; ABIIRBasicBlock &get(llvm::BasicBlock *BB) { auto It = BBMap.find(BB); if (It != BBMap.end()) return It->second; return BBMap.emplace(BB, ABIIRBasicBlock(BB)).first->second; } const ABIIRBasicBlock &get(llvm::BasicBlock *BB) const { auto It = BBMap.find(BB); revng_assert(It != BBMap.end()); return It->second; } calls_const_range calls() const { return llvm::make_range(Calls.begin(), Calls.end()); } size_t calls_size() const { return Calls.size(); } returns_const_range finals() const { return llvm::make_range(FinalBBs.begin(), FinalBBs.end()); } size_t finals_size() const { return FinalBBs.size(); } bool verify() const debug_function; /// \brief Dump a GraphViz file on stdout representing this function void dumpDot() const debug_function; void dump(const llvm::Module *M) const debug_function { dump(dbg, M); } template void dump(T &Output, const llvm::Module *M) const { std::set Visited; std::set Entries; for (auto &P : BBMap) if (P.second.predecessor_size() == 0) Entries.insert(&P.second); for (const ABIIRBasicBlock *BB : Entries) { if (Visited.count(BB) != 0) continue; std::stack WorkList; WorkList.push(BB); while (!WorkList.empty()) { const ABIIRBasicBlock *Current = WorkList.top(); WorkList.pop(); Visited.insert(Current); Current->dump(Output, M, " "); for (const ABIIRBasicBlock *Successor : Current->successors()) if (Visited.count(Successor) == 0) WorkList.push(Successor); } } } }; /// \brief Identify calls leading to contradition /// /// \note This is implemented in incoherentcallsanalysis.cpp std::set computeIncoherentCalls(ABIIRBasicBlock *Entry, std::vector &Extremals); template inline T instructionRange(ABIIRBasicBlock *BB); template<> inline ABIIRBasicBlock::range instructionRange(ABIIRBasicBlock *BB) { ABIIRBasicBlock::iterator InstructionIt = BB->begin(); return llvm::make_range(InstructionIt, BB->end()); } template<> inline ABIIRBasicBlock::reverse_range instructionRange(ABIIRBasicBlock *BB) { ABIIRBasicBlock::reverse_iterator InstructionIt = BB->rbegin(); return llvm::make_range(InstructionIt, BB->rend()); } } // namespace StackAnalysis // Provide graph traits for usage with, e.g., llvm::ReversePostOrderTraversal namespace llvm { template<> struct GraphTraits { using NodeRef = StackAnalysis::ABIIRBasicBlock *; using ChildIteratorType = StackAnalysis::ABIIRBasicBlock::links_iterator; static NodeRef getEntryNode(StackAnalysis::ABIIRBasicBlock *BB) { return BB; } static inline ChildIteratorType child_begin(StackAnalysis::ABIIRBasicBlock *N) { return N->successors().begin(); } static inline ChildIteratorType child_end(StackAnalysis::ABIIRBasicBlock *N) { return N->successors().end(); } }; template<> struct GraphTraits> { using NodeRef = StackAnalysis::ABIIRBasicBlock *; using ChildIteratorType = StackAnalysis::ABIIRBasicBlock::links_iterator; static NodeRef getEntryNode(StackAnalysis::ABIIRBasicBlock *BB) { return BB; } static inline ChildIteratorType child_begin(StackAnalysis::ABIIRBasicBlock *N) { return N->predecessor_begin(); } static inline ChildIteratorType child_end(StackAnalysis::ABIIRBasicBlock *N) { return N->predecessor_end(); } }; } // namespace llvm