Files
Alessandro Di Federico a0c670652c Improve usages of Expected<T>
2024-10-10 14:50:26 +02:00

210 lines
6.5 KiB
C++

#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<typename T>
concept ControlFlowGraphCacheTraits = requires {
typename T::BasicBlock;
typename T::Function;
typename T::CallInst;
typename T::KeyType;
{
T::getKey(std::declval<typename T::Function>())
} -> std::same_as<typename T::KeyType>;
{
T::getKey(std::declval<typename T::BasicBlock>())
} -> std::same_as<typename T::KeyType>;
{
T::getLocation(std::declval<typename T::CallInst>())
} -> std::same_as<
std::optional<pipeline::Location<decltype(revng::ranks::Instruction)>>>;
{
T::getFunction(std::declval<typename T::CallInst>())
} -> std::same_as<typename T::Function>;
{
T::getModelFunction(std::declval<const model::Binary &>(),
std::declval<typename T::Function>())
} -> std::same_as<const model::Function *>;
{
T::getFunctionAddress(std::declval<typename T::Function>())
} -> std::same_as<MetaAddress>;
};
/// 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<ControlFlowGraphCacheTraits Traits>
class BasicControlFlowGraphCache {
using BasicBlock = typename Traits::BasicBlock;
using Function = typename Traits::Function;
using CallInst = typename Traits::CallInst;
const revng::pipes::CFGMap &CFGs;
std::map<MetaAddress, TupleTree<efa::ControlFlowGraph>> Deserialized;
public:
BasicControlFlowGraphCache(const revng::pipes::CFGMap &CFGs) : CFGs(CFGs) {}
public:
void set(TupleTree<efa::ControlFlowGraph> &&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<efa::ControlFlowGraph>;
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<std::optional<efa::CallEdge>, 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<efa::CallEdge>(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<ConstOrNot<model::Binary> T>
inline ConstPtrIfConst<T, model::TypeDefinition>
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<decltype(revng::ranks::Instruction)>;
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<Location> 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<LLVMIRMetadataTraits>;
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<ControlFlowGraphCacheAnalysis> {
friend llvm::AnalysisInfoMixin<ControlFlowGraphCacheAnalysis>;
private:
ControlFlowGraphCache Cache;
static llvm::AnalysisKey Key;
public:
using Result = ControlFlowGraphCache;
public:
ControlFlowGraphCache *runOnModule(llvm::Module &M) { return &Cache; }
};