#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include "llvm/IR/Instructions.h" #include "llvm/IR/PassManager.h" #include "llvm/Pass.h" #include "revng/EarlyFunctionAnalysis/CFGStringMap.h" #include "revng/EarlyFunctionAnalysis/ControlFlowGraph.h" #include "revng/Model/Binary.h" #include "revng/Model/IRHelpers.h" #include "revng/Pipes/IRHelpers.h" #include "revng/Pipes/Kinds.h" #include "revng/Support/Assert.h" #include "revng/Support/IRHelpers.h" #include "revng/Support/MetaAddress.h" #include "revng/Support/YAMLTraits.h" #include "revng/TupleTree/TupleTree.h" template concept ControlFlowGraphCacheTraits = requires { typename T::BasicBlock; typename T::Function; typename T::CallInst; typename T::KeyType; { T::getKey(std::declval()) } -> std::same_as; { T::getKey(std::declval()) } -> std::same_as; { T::getLocation(std::declval()) } -> std::same_as< std::optional>>; { T::getFunction(std::declval()) } -> std::same_as; { T::getModelFunction(std::declval(), std::declval()) } -> std::same_as; { T::getFunctionAddress(std::declval()) } -> std::same_as; }; /// The BasicControlFlowGraphCache is implemented as a class template customised /// via a traits class in order to enable reuse for both LLVM IR and MLIR. template class BasicControlFlowGraphCache { using BasicBlock = typename Traits::BasicBlock; using Function = typename Traits::Function; using CallInst = typename Traits::CallInst; const revng::pipes::CFGMap &CFGs; std::map> Deserialized; public: BasicControlFlowGraphCache(const revng::pipes::CFGMap &CFGs) : CFGs(CFGs) {} public: void set(TupleTree &&New) { Deserialized[New->Entry()] = std::move(New); } public: const efa::ControlFlowGraph &getControlFlowGraph(const MetaAddress &Address) { auto It = Deserialized.find(Address); if (It != Deserialized.end()) return *It->second.get(); using TupleTree = TupleTree; TupleTree &Result = Deserialized[Address]; Result = cantFail(TupleTree::fromString(CFGs.at(Address))); return *Result.get(); } const efa::ControlFlowGraph &getControlFlowGraph(const Function Function) { return getControlFlowGraph(Traits::getFunctionAddress(Function)); } /// Given a Call instruction and the model type of its parent function, return /// the edge on the model that represents that call (std::nullopt if this /// doesn't exist) and the BasicBlockID associated to the call-site. inline std::pair, BasicBlockID> getCallEdge(const model::Binary &Binary, CallInst Call) { using namespace llvm; auto MaybeLocation = Traits::getLocation(Call); if (not MaybeLocation) return { std::nullopt, BasicBlockID::invalid() }; auto BlockAddress = MaybeLocation->parent().back(); Function ParentFunction = Traits::getFunction(Call); const efa::ControlFlowGraph &FM = getControlFlowGraph(ParentFunction); const efa::BasicBlock &Block = FM.Blocks().at(BlockAddress); // Find the call edge const efa::CallEdge *ModelCall = nullptr; for (auto &Edge : Block.Successors()) { if (auto *CE = dyn_cast(Edge.get())) { revng_assert(ModelCall == nullptr); ModelCall = CE; } } revng_assert(ModelCall != nullptr); return { *ModelCall, Block.ID() }; } /// \return the prototype associated to a CallInst. /// /// \note If the model type of the parent function is not provided, this will /// be deduced using the Call instruction's parent function. /// /// \note If the call site has no associated prototype, e.g. the called /// function is not an isolated function, a null pointer is returned. template T> inline ConstPtrIfConst getCallSitePrototype(T &Binary, CallInst Call, const model::Function *ParentFunction = nullptr) { if (not ParentFunction) ParentFunction = Traits::getModelFunction(Binary, Traits::getFunction(Call)); if (not ParentFunction) return nullptr; const auto &[Edge, BlockAddress] = getCallEdge(Binary, Call); if (not Edge) return nullptr; return getPrototype(Binary, ParentFunction->Entry(), BlockAddress, *Edge); } }; class LLVMIRMetadataTraits { using Location = pipeline::Location; public: using BasicBlock = const llvm::BasicBlock *; using Function = const llvm::Function *; using CallInst = const llvm::CallInst *; using KeyType = const llvm::Value *; static KeyType getKey(Function F) { return F; } static KeyType getKey(BasicBlock BB) { return BB; } static const llvm::Function *getFunction(const llvm::Instruction *const I) { return I->getFunction(); } static std::optional getLocation(const llvm::Instruction *const I) { return ::getLocation(I); } static const model::Function *getModelFunction(const model::Binary &Binary, const llvm::Function *F) { return llvmToModelFunction(Binary, *F); } static MetaAddress getFunctionAddress(const llvm::Function *F) { return getMetaAddressOfIsolatedFunction(*F); } }; using ControlFlowGraphCache = BasicControlFlowGraphCache; class ControlFlowGraphCachePass : public llvm::ImmutablePass { public: static char ID; private: ControlFlowGraphCache Cache; public: ControlFlowGraphCachePass(const revng::pipes::CFGMap &CFGs) : llvm::ImmutablePass(ID), Cache(CFGs) {} ControlFlowGraphCache &get() { return Cache; } }; class ControlFlowGraphCacheAnalysis : public llvm::AnalysisInfoMixin { friend llvm::AnalysisInfoMixin; private: ControlFlowGraphCache Cache; static llvm::AnalysisKey Key; public: using Result = ControlFlowGraphCache; public: ControlFlowGraphCache *runOnModule(llvm::Module &M) { return &Cache; } };