mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
c7f8f3302d
This commit introduces `BasicBlockID` as the unique identifier for a `efa::BasicBlock` into the CFG. A `BasicBlockID` is defined by a `MetaAddress` plus an incremental integer. This enables us to have multiple instances of the same block in a single function, which is particularly useful when inlining multiple times the same function. Apart from this, the commit also does the following: * It drops representing `MetaAddress`es a `structs` in the IR. This created several issues related to ABI. We now represent them as strings. * It defines more functions in `support.h`, instead of defining prototypes by hand in `CodeGenerator.cpp` and the like. Specifically, `unknownPC` and `raise_exception_helper`. We also introduce a C "constructor" for `PlainMetaAddress`. * It significantly reduces the API of `GeneratedCodeBasicInfo`, which was supposed to be put on a diet since a long time. Specifically, many jump target related methods have been moved to free functions in `IRHelpers.h`. Also `GCBI::getSuccessors` has been pushed into its only user, `PruneRetSuccessors`, to prevent further usage of a deprecated API. In the future, it would be nice to drop it entirely. * It introduces `efa::BasicBlock::InlinedFrom`. * Introduce an enum to represent named argument indices for `newpc`. This enables us to more effectively manipulate its argument list. * It improves the verification and error reporting for `efa::FunctionMetadata`. * Update tests. This commit is preliminary to another piece of work to improve the generality of inlining beyond the simple "fake function" scenario, for which the feature was originally conceived.
381 lines
11 KiB
C++
381 lines
11 KiB
C++
#pragma once
|
|
|
|
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
#include <cstdint>
|
|
#include <map>
|
|
#include <utility>
|
|
|
|
#include "llvm/ADT/Any.h"
|
|
#include "llvm/ADT/DepthFirstIterator.h"
|
|
#include "llvm/IR/Dominators.h"
|
|
#include "llvm/IR/GlobalObject.h"
|
|
#include "llvm/IR/PassManager.h"
|
|
#include "llvm/Pass.h"
|
|
#include "llvm/Support/Casting.h"
|
|
|
|
#include "revng/ADT/Concepts.h"
|
|
#include "revng/Lift/Lift.h"
|
|
#include "revng/Model/Architecture.h"
|
|
#include "revng/Model/Binary.h"
|
|
#include "revng/Model/LoadModelPass.h"
|
|
#include "revng/Support/BlockType.h"
|
|
#include "revng/Support/IRHelpers.h"
|
|
#include "revng/Support/ProgramCounterHandler.h"
|
|
|
|
// Forward declarations
|
|
namespace llvm {
|
|
class BasicBlock;
|
|
class GlobalVariable;
|
|
class Instruction;
|
|
class MDNode;
|
|
} // namespace llvm
|
|
|
|
/// \brief Pass to collect basic information about the generated code
|
|
///
|
|
/// This pass provides useful information for other passes by extracting them
|
|
/// from the generated IR, and possibly caching them.
|
|
///
|
|
/// It provides details about the input architecture such as the size of its
|
|
/// delay slot, the name of the program counter register and so on. It also
|
|
/// provides information about the generated basic blocks, distinguishing
|
|
/// between basic blocks generated due to translation and dispatcher-related
|
|
/// basic blocks.
|
|
class GeneratedCodeBasicInfo {
|
|
public:
|
|
GeneratedCodeBasicInfo(const model::Binary &Binary) :
|
|
Binary(&Binary),
|
|
PC(nullptr),
|
|
SP(nullptr),
|
|
RA(nullptr),
|
|
Dispatcher(nullptr),
|
|
DispatcherFail(nullptr),
|
|
AnyPC(nullptr),
|
|
UnexpectedPC(nullptr),
|
|
PCRegSize(0),
|
|
RootFunction(nullptr),
|
|
PCH(),
|
|
RootParsed(false) {}
|
|
|
|
void run(llvm::Module &M);
|
|
|
|
/// \brief Handle the invalidation of this information, so that it does not
|
|
/// get invalidated by other passes.
|
|
bool invalidate(llvm::Module &,
|
|
const llvm::PreservedAnalyses &,
|
|
llvm::ModuleAnalysisManager::Invalidator &) {
|
|
return false;
|
|
}
|
|
|
|
bool invalidate(llvm::Function &,
|
|
const llvm::PreservedAnalyses &,
|
|
llvm::FunctionAnalysisManager::Invalidator &) {
|
|
return false;
|
|
}
|
|
|
|
uint32_t getJTReasons(llvm::BasicBlock *BB) const {
|
|
return getJTReasons(BB->getTerminator());
|
|
}
|
|
|
|
uint32_t getJTReasons(llvm::Instruction *T) const {
|
|
using namespace llvm;
|
|
|
|
revng_assert(T->isTerminator());
|
|
|
|
uint32_t Result = 0;
|
|
|
|
MDNode *Node = T->getMetadata(JTReasonMDName);
|
|
auto *Tuple = cast_or_null<MDTuple>(Node);
|
|
revng_assert(Tuple != nullptr);
|
|
|
|
for (Metadata *ReasonMD : Tuple->operands()) {
|
|
StringRef Text = cast<MDString>(ReasonMD)->getString();
|
|
Result |= static_cast<uint32_t>(JTReason::fromName(Text));
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
KillReason::Values getKillReason(llvm::BasicBlock *BB) const {
|
|
return getKillReason(BB->getTerminator());
|
|
}
|
|
|
|
KillReason::Values getKillReason(llvm::Instruction *T) const {
|
|
using namespace llvm;
|
|
|
|
revng_assert(T->isTerminator());
|
|
|
|
auto *NoReturnMD = T->getMetadata("noreturn");
|
|
if (auto *NoreturnTuple = dyn_cast_or_null<MDTuple>(NoReturnMD)) {
|
|
QuickMetadata QMD(getContext(T));
|
|
return KillReason::fromName(QMD.extract<StringRef>(NoreturnTuple, 0));
|
|
}
|
|
|
|
return KillReason::NonKiller;
|
|
}
|
|
|
|
bool isKiller(llvm::BasicBlock *BB) const {
|
|
return isKiller(BB->getTerminator());
|
|
}
|
|
|
|
bool isKiller(llvm::Instruction *T) const {
|
|
revng_assert(T->isTerminator());
|
|
return getKillReason(T) != KillReason::NonKiller;
|
|
}
|
|
|
|
/// \brief Return the CSV representing the stack pointer
|
|
llvm::GlobalVariable *spReg() const { return SP; }
|
|
/// \brief Return the CSV representing the return address register
|
|
llvm::GlobalVariable *raReg() const { return RA; }
|
|
|
|
/// \brief Check if \p GV is the stack pointer CSV
|
|
bool isSPReg(const llvm::GlobalVariable *GV) const {
|
|
revng_assert(SP != nullptr);
|
|
return GV == SP;
|
|
}
|
|
|
|
bool isSPReg(const llvm::Value *V) const {
|
|
if (auto *GV = llvm::dyn_cast<const llvm::GlobalVariable>(V))
|
|
return isSPReg(GV);
|
|
return false;
|
|
}
|
|
|
|
// TODO: this method should probably be deprecated
|
|
/// \brief Return the CSV representing the program counter
|
|
llvm::GlobalVariable *pcReg() const { return PC; }
|
|
|
|
// TODO: this method should probably be deprecated
|
|
/// \brief Check if \p GV is the program counter CSV
|
|
bool isPCReg(const llvm::GlobalVariable *GV) const {
|
|
revng_assert(PC != nullptr);
|
|
return GV == PC;
|
|
}
|
|
|
|
// TODO: this method should probably be deprecated
|
|
bool isServiceRegister(const llvm::Value *V) const {
|
|
auto *GV = llvm::dyn_cast<llvm::GlobalVariable>(V);
|
|
return GV != nullptr and (isPCReg(GV) or isSPReg(GV));
|
|
}
|
|
|
|
const ProgramCounterHandler *programCounterHandler() {
|
|
if (not PCH) {
|
|
llvm::Module *M = RootFunction->getParent();
|
|
using namespace model::Architecture;
|
|
auto Architecture = toLLVMArchitecture(Binary->Architecture());
|
|
PCH = ProgramCounterHandler::fromModule(Architecture, M);
|
|
}
|
|
|
|
return PCH.get();
|
|
}
|
|
|
|
template<typename T>
|
|
ProgramCounterHandler::DispatcherInfo
|
|
buildDispatcher(T &Targets,
|
|
llvm::IRBuilder<> &Builder,
|
|
llvm::BasicBlock *Default = nullptr) {
|
|
parseRoot();
|
|
|
|
ProgramCounterHandler::DispatcherTargets TargetsPairs;
|
|
TargetsPairs.reserve(Targets.size());
|
|
for (MetaAddress MA : Targets)
|
|
TargetsPairs.push_back({ MA, getBlockAt(MA) });
|
|
|
|
if (Default == nullptr)
|
|
Default = UnexpectedPC;
|
|
|
|
auto IBDHB = BlockType::IndirectBranchDispatcherHelperBlock;
|
|
return programCounterHandler()->buildDispatcher(TargetsPairs,
|
|
Builder,
|
|
Default,
|
|
{ IBDHB });
|
|
}
|
|
|
|
/// \brief Return the basic block associated to \p PC
|
|
///
|
|
/// Returns nullptr if the PC doesn't have a basic block (yet)
|
|
llvm::BasicBlock *getBlockAt(MetaAddress PC) {
|
|
parseRoot();
|
|
|
|
auto It = JumpTargets.find(PC);
|
|
if (It == JumpTargets.end())
|
|
return nullptr;
|
|
|
|
return It->second;
|
|
}
|
|
|
|
bool isJump(llvm::BasicBlock *BB) { return isJump(BB->getTerminator()); }
|
|
|
|
/// \brief Return true if \p T represents a jump in the input assembly
|
|
///
|
|
/// Return true if \p T targets include only dispatcher-related basic blocks
|
|
/// and jump targets.
|
|
bool isJump(llvm::Instruction *T) {
|
|
parseRoot();
|
|
revng_assert(T->getParent()->getParent() == RootFunction);
|
|
revng_assert(T != nullptr);
|
|
revng_assert(T->isTerminator());
|
|
|
|
for (llvm::BasicBlock *Successor : successors(T)) {
|
|
if (not(Successor->empty() or Successor == Dispatcher
|
|
or Successor == DispatcherFail or Successor == AnyPC
|
|
or Successor == UnexpectedPC or isJumpTarget(Successor)))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// \brief Return true if \p BB is the result of translating some code
|
|
///
|
|
/// Return false if \p BB is a dispatcher-related basic block.
|
|
static bool isTranslated(llvm::BasicBlock *BB) {
|
|
BlockType::Values Type = getType(BB);
|
|
return (Type == BlockType::TranslatedBlock
|
|
or Type == BlockType::JumpTargetBlock);
|
|
}
|
|
|
|
/// \brief Return the program counter of the next (i.e., fallthrough)
|
|
/// instruction of \p TheInstruction
|
|
MetaAddress getNextPC(llvm::Instruction *TheInstruction) const {
|
|
auto Pair = getPC(TheInstruction);
|
|
return Pair.first + Pair.second;
|
|
}
|
|
|
|
llvm::BasicBlock *getCallReturnBlock(llvm::BasicBlock *BB) const {
|
|
using namespace llvm;
|
|
CallInst *FunctionCallMarker = getMarker(BB, "function_call");
|
|
revng_assert(FunctionCallMarker != nullptr);
|
|
auto *FallthroughBA = cast<BlockAddress>(FunctionCallMarker->getOperand(1));
|
|
return FallthroughBA->getBasicBlock();
|
|
}
|
|
|
|
auto getBlocksGeneratedByPC(MetaAddress PC) {
|
|
using namespace llvm;
|
|
BasicBlock *Entry = getBlockAt(PC);
|
|
revng_assert(isJumpTarget(Entry));
|
|
std::set<BasicBlock *> Result;
|
|
|
|
llvm::df_iterator_default_set<BasicBlock *> Visited;
|
|
for (BasicBlock *BB : llvm::depth_first_ext(Entry, Visited)) {
|
|
Result.insert(BB);
|
|
|
|
for (BasicBlock *Successor : successors(BB)) {
|
|
const auto IBDHB = BlockType::IndirectBranchDispatcherHelperBlock;
|
|
if (isJumpTarget(Successor)
|
|
or (not isTranslated(Successor) and getType(Successor) != IBDHB)) {
|
|
Visited.insert(Successor);
|
|
}
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
llvm::BasicBlock *anyPC() {
|
|
parseRoot();
|
|
return AnyPC;
|
|
}
|
|
|
|
llvm::BasicBlock *unexpectedPC() {
|
|
parseRoot();
|
|
return UnexpectedPC;
|
|
}
|
|
|
|
llvm::BasicBlock *dispatcher() {
|
|
parseRoot();
|
|
return Dispatcher;
|
|
}
|
|
|
|
const llvm::ArrayRef<llvm::GlobalVariable *> csvs() const { return CSVs; }
|
|
|
|
const std::vector<llvm::GlobalVariable *> &abiRegisters() const {
|
|
return ABIRegisters;
|
|
}
|
|
|
|
bool isABIRegister(llvm::GlobalVariable *CSV) const {
|
|
return ABIRegistersSet.count(CSV) != 0;
|
|
}
|
|
|
|
MetaAddress fromPC(uint64_t PC) const {
|
|
using namespace model::Architecture;
|
|
auto Architecture = toLLVMArchitecture(Binary->Architecture());
|
|
return MetaAddress::fromPC(Architecture, PC);
|
|
}
|
|
|
|
llvm::Function *root() {
|
|
parseRoot();
|
|
return RootFunction;
|
|
}
|
|
|
|
llvm::SmallVector<std::pair<llvm::BasicBlock *, bool>, 4>
|
|
blocksByPCRange(MetaAddress Start, MetaAddress End);
|
|
|
|
private:
|
|
void parseRoot();
|
|
|
|
private:
|
|
const model::Binary *Binary;
|
|
llvm::GlobalVariable *PC;
|
|
llvm::GlobalVariable *SP;
|
|
llvm::GlobalVariable *RA;
|
|
llvm::BasicBlock *Dispatcher;
|
|
llvm::BasicBlock *DispatcherFail;
|
|
llvm::BasicBlock *AnyPC;
|
|
llvm::BasicBlock *UnexpectedPC;
|
|
std::map<MetaAddress, llvm::BasicBlock *> JumpTargets;
|
|
unsigned PCRegSize;
|
|
llvm::Function *RootFunction;
|
|
std::vector<llvm::GlobalVariable *> CSVs;
|
|
std::vector<llvm::GlobalVariable *> ABIRegisters;
|
|
std::set<llvm::GlobalVariable *> ABIRegistersSet;
|
|
llvm::Function *NewPC;
|
|
std::unique_ptr<ProgramCounterHandler> PCH;
|
|
using PCToBlockMap = std::multimap<MetaAddress, llvm::BasicBlock *>;
|
|
bool RootParsed = false;
|
|
};
|
|
|
|
template<>
|
|
struct BlackListTrait<const GeneratedCodeBasicInfo &, llvm::BasicBlock *>
|
|
: BlackListTraitBase<const GeneratedCodeBasicInfo &> {
|
|
using BlackListTraitBase<const GeneratedCodeBasicInfo &>::BlackListTraitBase;
|
|
bool isBlacklisted(llvm::BasicBlock *Value) const {
|
|
return !this->Obj.isTranslated(Value);
|
|
}
|
|
};
|
|
|
|
/// An analysis pass that computes a \c GCBI result. The result of
|
|
/// this analysis is invalidated each time the analysis is called.
|
|
class GeneratedCodeBasicInfoAnalysis
|
|
: public llvm::AnalysisInfoMixin<GeneratedCodeBasicInfoAnalysis> {
|
|
friend llvm::AnalysisInfoMixin<GeneratedCodeBasicInfoAnalysis>;
|
|
static llvm::AnalysisKey Key;
|
|
|
|
public:
|
|
using Result = GeneratedCodeBasicInfo;
|
|
/// \note If a MPM is used, then make sure to register the
|
|
/// analysis manually and use a proxy.
|
|
Result run(llvm::Module &M, llvm::ModuleAnalysisManager &);
|
|
Result run(llvm::Function &F, llvm::FunctionAnalysisManager &);
|
|
};
|
|
|
|
/// Legacy pass manager pass to access GCBI.
|
|
class GeneratedCodeBasicInfoWrapperPass : public llvm::ModulePass {
|
|
std::unique_ptr<GeneratedCodeBasicInfo> GCBI;
|
|
|
|
public:
|
|
static char ID;
|
|
|
|
GeneratedCodeBasicInfoWrapperPass() : llvm::ModulePass(ID) {}
|
|
|
|
GeneratedCodeBasicInfo &getGCBI() { return *GCBI; }
|
|
|
|
bool runOnModule(llvm::Module &M) override;
|
|
void releaseMemory() override;
|
|
void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
|
|
AU.setPreservesAll();
|
|
AU.addRequired<LoadModelWrapperPass>();
|
|
}
|
|
};
|