mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
1410 lines
41 KiB
C++
1410 lines
41 KiB
C++
#pragma once
|
|
|
|
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
#include <queue>
|
|
#include <set>
|
|
#include <sstream>
|
|
#include <type_traits>
|
|
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/iterator_range.h"
|
|
#include "llvm/Analysis/ConstantFolding.h"
|
|
#include "llvm/Analysis/Interval.h"
|
|
#include "llvm/IR/CFG.h"
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/DataLayout.h"
|
|
#include "llvm/IR/Instruction.h"
|
|
#include "llvm/IR/Instructions.h"
|
|
#include "llvm/IR/Metadata.h"
|
|
#include "llvm/IR/Module.h"
|
|
#include "llvm/IR/ValueMap.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
#include "revng/ADT/Concepts.h"
|
|
#include "revng/Support/Debug.h"
|
|
#include "revng/Support/FunctionTags.h"
|
|
#include "revng/Support/Generator.h"
|
|
#include "revng/Support/MetaAddress.h"
|
|
|
|
extern void dumpUsers(llvm::Value *V) debug_function;
|
|
|
|
/// Given \p V, checks if there are uses left and then calls eraseFromParent.
|
|
/// In case of leftover uses, they are pretty printed.
|
|
///
|
|
/// \note The remaining use check is not performed on llvm::Functions since they
|
|
/// might have internal blockaddress self-references.
|
|
inline void eraseFromParent(llvm::Value *V) {
|
|
using namespace llvm;
|
|
|
|
if (not isa<Function>(V) and not V->use_empty()) {
|
|
dbg << "Can't erase a Value still having uses.\n";
|
|
dbg << "Value:\n ";
|
|
V->dump();
|
|
dbg << "Users:\n";
|
|
dumpUsers(V);
|
|
revng_abort();
|
|
} else {
|
|
if (auto *I = dyn_cast<Instruction>(V))
|
|
I->eraseFromParent();
|
|
else if (auto *BB = dyn_cast<BasicBlock>(V))
|
|
BB->eraseFromParent();
|
|
else if (auto *G = dyn_cast<GlobalValue>(V))
|
|
G->eraseFromParent();
|
|
else
|
|
revng_abort();
|
|
}
|
|
}
|
|
|
|
constexpr const char *FunctionEntryMDNName = "revng.function.entry";
|
|
constexpr const char *CallerBlockStartMDName = "revng.callerblock.start";
|
|
constexpr const char *JTReasonMDName = "revng.jt.reasons";
|
|
constexpr const char *FunctionMetadataMDName = "revng.function.metadata";
|
|
|
|
template<typename T>
|
|
inline bool contains(T Range, typename T::value_type V) {
|
|
return std::find(std::begin(Range), std::end(Range), V) != std::end(Range);
|
|
}
|
|
|
|
template<class T>
|
|
inline void freeContainer(T &Container) {
|
|
T Empty;
|
|
Empty.swap(Container);
|
|
}
|
|
|
|
/// Helper function to destroy an unconditional branch and, in case, the target
|
|
/// basic block, if it doesn't have any predecessors left.
|
|
inline void purgeBranch(llvm::BasicBlock::iterator I) {
|
|
auto *DeadBranch = llvm::dyn_cast<llvm::BranchInst>(I);
|
|
// We allow only a branch and nothing else
|
|
revng_assert(DeadBranch != nullptr && ++I == DeadBranch->getParent()->end());
|
|
|
|
std::set<llvm::BasicBlock *> Successors;
|
|
for (unsigned C = 0; C < DeadBranch->getNumSuccessors(); C++)
|
|
Successors.insert(DeadBranch->getSuccessor(C));
|
|
|
|
// Destroy the dead branch
|
|
eraseFromParent(DeadBranch);
|
|
|
|
// Check if someone else was jumping there and then destroy
|
|
for (llvm::BasicBlock *BB : Successors)
|
|
if (BB->empty() && llvm::pred_empty(BB))
|
|
eraseFromParent(BB);
|
|
}
|
|
|
|
inline llvm::ConstantInt *
|
|
getConstValue(llvm::Constant *C, const llvm::DataLayout &DL) {
|
|
while (auto *Expr = llvm::dyn_cast<llvm::ConstantExpr>(C)) {
|
|
C = ConstantFoldConstant(Expr, DL);
|
|
|
|
if (Expr->isCast())
|
|
C = Expr->getOperand(0);
|
|
}
|
|
|
|
if (llvm::isa<llvm::ConstantPointerNull>(C)) {
|
|
auto *Ptr = llvm::IntegerType::get(C->getType()->getContext(),
|
|
DL.getPointerSizeInBits());
|
|
return llvm::ConstantInt::get(Ptr, 0);
|
|
}
|
|
|
|
auto *Integer = llvm::cast<llvm::ConstantInt>(C);
|
|
return Integer;
|
|
}
|
|
|
|
inline uint64_t getSExtValue(llvm::Constant *C, const llvm::DataLayout &DL) {
|
|
return getConstValue(C, DL)->getSExtValue();
|
|
}
|
|
|
|
inline uint64_t getZExtValue(llvm::Constant *C, const llvm::DataLayout &DL) {
|
|
return getConstValue(C, DL)->getZExtValue();
|
|
}
|
|
|
|
inline uint64_t
|
|
getExtValue(llvm::Constant *C, bool Sign, const llvm::DataLayout &DL) {
|
|
if (Sign)
|
|
return getSExtValue(C, DL);
|
|
else
|
|
return getZExtValue(C, DL);
|
|
}
|
|
|
|
inline uint64_t getLimitedValue(const llvm::Value *V) {
|
|
return llvm::cast<llvm::ConstantInt>(V)->getLimitedValue();
|
|
}
|
|
|
|
inline uint64_t getSignedLimitedValue(const llvm::Value *V) {
|
|
return llvm::cast<llvm::ConstantInt>(V)->getSExtValue();
|
|
}
|
|
|
|
template<typename T, unsigned I>
|
|
inline bool findOperand(llvm::Value *Op, T &Result) {
|
|
return false;
|
|
}
|
|
|
|
template<typename T, unsigned I, typename Head, typename... Tail>
|
|
inline bool findOperand(llvm::Value *Op, T &Result) {
|
|
using VT = typename std::remove_pointer<Head>::type;
|
|
if (auto *Casted = llvm::dyn_cast<VT>(Op)) {
|
|
std::get<I>(Result) = Casted;
|
|
return true;
|
|
} else {
|
|
return findOperand<T, I + 1, Tail...>(Op, Result);
|
|
}
|
|
}
|
|
|
|
/// \brief Return a tuple of \p V's operands of the requested types
|
|
/// \return a tuple with the operands of the specified type in the specified
|
|
/// order, or, if not possible, a nullptr tuple.
|
|
template<typename... T>
|
|
inline std::tuple<T...> operandsByType(llvm::User *V) {
|
|
std::tuple<T...> Result;
|
|
unsigned OpCount = V->getNumOperands();
|
|
revng_assert(OpCount == sizeof...(T));
|
|
|
|
for (llvm::Value *Op : V->operands())
|
|
if (!findOperand<std::tuple<T...>, 0, T...>(Op, Result))
|
|
return std::tuple<T...>{};
|
|
|
|
return Result;
|
|
}
|
|
|
|
/// \brief Checks the instruction type and its operands
|
|
/// \return the instruction casted to I, or nullptr if not possible.
|
|
template<typename I, typename F, typename S>
|
|
inline I *isa_with_op(llvm::Instruction *Inst) {
|
|
if (auto *Casted = llvm::dyn_cast<I>(Inst)) {
|
|
revng_assert(Casted->getNumOperands() == 2);
|
|
if (llvm::isa<F>(Casted->getOperand(0))
|
|
&& llvm::isa<S>(Casted->getOperand(1))) {
|
|
return Casted;
|
|
} else if (llvm::isa<F>(Casted->getOperand(0))
|
|
&& llvm::isa<S>(Casted->getOperand(1))) {
|
|
revng_assert(Casted->isCommutative());
|
|
Casted->swapOperands();
|
|
return Casted;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
template<typename C>
|
|
struct BlackListTraitBase {
|
|
BlackListTraitBase(C Obj) : Obj(Obj) {}
|
|
|
|
protected:
|
|
C Obj;
|
|
};
|
|
|
|
/// \brief Trait to wrap an object of type C that can act as a blacklist for B
|
|
template<typename C, typename B>
|
|
struct BlackListTrait : BlackListTraitBase<C> {};
|
|
|
|
class NullBlackList {};
|
|
|
|
template<typename B>
|
|
struct BlackListTrait<const NullBlackList &, B>
|
|
: BlackListTraitBase<const NullBlackList &> {
|
|
using BlackListTraitBase<const NullBlackList &>::BlackListTraitBase;
|
|
bool isBlacklisted(B Value) const { return false; }
|
|
};
|
|
|
|
template<typename C>
|
|
struct BlackListTrait<C, C> : BlackListTraitBase<C> {
|
|
using BlackListTraitBase<C>::BlackListTraitBase;
|
|
bool isBlacklisted(C Value) const { return Value == this->Obj; }
|
|
};
|
|
|
|
template<typename B>
|
|
struct BlackListTrait<const std::set<B> &, B>
|
|
: BlackListTraitBase<const std::set<B> &> {
|
|
using BlackListTraitBase<const std::set<B> &>::BlackListTraitBase;
|
|
bool isBlacklisted(B Value) const { return this->Obj.count(Value) != 0; }
|
|
};
|
|
|
|
template<typename B, typename C>
|
|
inline BlackListTrait<C, B> make_blacklist(C Obj) {
|
|
return BlackListTrait<C, B>(Obj);
|
|
}
|
|
|
|
template<typename B>
|
|
inline BlackListTrait<const std::set<B> &, B>
|
|
make_blacklist(const std::set<B> &Obj) {
|
|
return BlackListTrait<const std::set<B> &, B>(Obj);
|
|
}
|
|
|
|
/// \brief Possible way to continue (or stop) exploration in a breadth-first
|
|
/// visit
|
|
enum VisitAction {
|
|
Continue, ///< Visit also the successor basic blocks
|
|
NoSuccessors, ///< Do not visit the successors of this basic block
|
|
ExhaustQueueAndStop, ///< Prevent adding visiting other basic blocks except
|
|
/// those already pending
|
|
StopNow ///< Interrupt immediately the visit
|
|
};
|
|
|
|
using BasicBlockRange = llvm::iterator_range<llvm::BasicBlock::iterator>;
|
|
using VisitorFunction = std::function<VisitAction(BasicBlockRange)>;
|
|
|
|
template<bool Forward>
|
|
struct IteratorDirection {};
|
|
|
|
template<>
|
|
struct IteratorDirection<true> {
|
|
|
|
static llvm::BasicBlock::iterator iterator(llvm::Instruction *I) {
|
|
return ++llvm::BasicBlock::iterator(I);
|
|
}
|
|
|
|
static llvm::BasicBlock::iterator begin(llvm::BasicBlock *BB) {
|
|
return BB->begin();
|
|
}
|
|
|
|
static llvm::BasicBlock::iterator end(llvm::BasicBlock *BB) {
|
|
return BB->end();
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct IteratorDirection<false> {
|
|
|
|
static llvm::BasicBlock::reverse_iterator iterator(llvm::Instruction *I) {
|
|
return llvm::BasicBlock::reverse_iterator(++I->getReverseIterator());
|
|
}
|
|
|
|
static llvm::BasicBlock::reverse_iterator begin(llvm::BasicBlock *BB) {
|
|
return BB->rbegin();
|
|
}
|
|
|
|
static llvm::BasicBlock::reverse_iterator end(llvm::BasicBlock *BB) {
|
|
return BB->rend();
|
|
}
|
|
};
|
|
|
|
template<bool Forward, typename Derived, typename SuccessorsRange>
|
|
struct BFSVisitorBase {
|
|
public:
|
|
using BasicBlock = llvm::BasicBlock;
|
|
using forward_iterator = BasicBlock::iterator;
|
|
using backward_iterator = BasicBlock::reverse_iterator;
|
|
template<bool C, typename A, typename B>
|
|
using conditional_t = std::conditional_t<C, A, B>;
|
|
using instruction_iterator = conditional_t<Forward,
|
|
forward_iterator,
|
|
backward_iterator>;
|
|
using instruction_range = llvm::iterator_range<instruction_iterator>;
|
|
|
|
void run(llvm::Instruction *I) {
|
|
auto &ThisDerived = *static_cast<Derived *>(this);
|
|
std::set<BasicBlock *> Visited;
|
|
|
|
using ID = IteratorDirection<Forward>;
|
|
instruction_iterator It = ID::iterator(I);
|
|
|
|
if (not Forward)
|
|
It--;
|
|
|
|
struct WorkItem {
|
|
WorkItem(BasicBlock *BB, instruction_iterator Start) :
|
|
BB(BB), Range(make_range(Start, ID::end(BB))) {}
|
|
|
|
WorkItem(BasicBlock *BB) :
|
|
BB(BB), Range(make_range(ID::begin(BB), ID::end(BB))) {}
|
|
|
|
BasicBlock *BB;
|
|
instruction_range Range;
|
|
};
|
|
|
|
std::queue<WorkItem> Queue;
|
|
Queue.push(WorkItem(I->getParent(), It));
|
|
|
|
bool ExhaustOnly = false;
|
|
|
|
while (not Queue.empty()) {
|
|
WorkItem Item = Queue.front();
|
|
Queue.pop();
|
|
|
|
switch (ThisDerived.visit(Item.Range)) {
|
|
case Continue:
|
|
if (not ExhaustOnly) {
|
|
for (auto *Successor : ThisDerived.successors(Item.BB)) {
|
|
if (Visited.count(Successor) == 0) {
|
|
Visited.insert(Successor);
|
|
Queue.push(WorkItem(Successor));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case NoSuccessors:
|
|
break;
|
|
case ExhaustQueueAndStop:
|
|
ExhaustOnly = true;
|
|
break;
|
|
case StopNow:
|
|
return;
|
|
default:
|
|
revng_abort();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
template<typename Derived>
|
|
struct BackwardBFSVisitor
|
|
: public BFSVisitorBase<false,
|
|
Derived,
|
|
llvm::iterator_range<llvm::pred_iterator>> {
|
|
|
|
llvm::iterator_range<llvm::pred_iterator> successors(llvm::BasicBlock *BB) {
|
|
return llvm::make_range(pred_begin(BB), pred_end(BB));
|
|
}
|
|
};
|
|
|
|
template<typename Derived>
|
|
struct ForwardBFSVisitor
|
|
: public BFSVisitorBase<true,
|
|
Derived,
|
|
llvm::iterator_range<llvm::succ_iterator>> {
|
|
|
|
llvm::iterator_range<llvm::succ_iterator> successors(llvm::BasicBlock *BB) {
|
|
return llvm::make_range(succ_begin(BB), succ_end(BB));
|
|
}
|
|
};
|
|
|
|
inline std::string getName(const llvm::Value *V);
|
|
|
|
/// \brief Return a string with the value of a given integer constant.
|
|
inline std::string getName(const llvm::ConstantInt *I) {
|
|
return std::to_string(I->getValue().getZExtValue());
|
|
}
|
|
|
|
/// \brief Return a sensible name for the given basic block
|
|
/// \return the name of the basic block, if available, its pointer value
|
|
/// otherwise.
|
|
inline std::string getName(const llvm::BasicBlock *BB) {
|
|
if (BB == nullptr)
|
|
return "(nullptr)";
|
|
|
|
llvm::StringRef Result = BB->getName();
|
|
if (!Result.empty()) {
|
|
return Result.str();
|
|
} else {
|
|
std::stringstream SS;
|
|
SS << "0x" << std::hex << intptr_t(BB);
|
|
return SS.str();
|
|
}
|
|
}
|
|
|
|
/// \brief Return a sensible name for the given instruction
|
|
/// \return the name of the instruction, if available, a
|
|
/// [basic blockname]:[instruction index] string otherwise.
|
|
inline std::string getName(const llvm::Instruction *I) {
|
|
llvm::StringRef Result = I->getName();
|
|
if (!Result.empty()) {
|
|
return Result.str();
|
|
} else if (const llvm::BasicBlock *Parent = I->getParent()) {
|
|
return getName(Parent) + ":"
|
|
+ std::to_string(1
|
|
+ std::distance(Parent->begin(), I->getIterator()));
|
|
} else {
|
|
std::stringstream SS;
|
|
SS << "0x" << std::hex << intptr_t(I);
|
|
return SS.str();
|
|
}
|
|
}
|
|
|
|
/// \brief Return a sensible name for the given function
|
|
/// \return the name of the function, if available, its pointer value otherwise.
|
|
inline std::string getName(const llvm::Function *F) {
|
|
if (F == nullptr)
|
|
return "(nullptr)";
|
|
|
|
if (F->hasName())
|
|
return F->getName().str();
|
|
|
|
std::stringstream SS;
|
|
SS << "0x" << std::hex << intptr_t(F);
|
|
return SS.str();
|
|
}
|
|
|
|
/// \brief Return a sensible name for the given argument
|
|
/// \return the name of the argument, if available, a
|
|
/// [function name]:[argument index] string otherwise.
|
|
inline std::string getName(const llvm::Argument *A) {
|
|
if (nullptr == A)
|
|
return "(nullptr)";
|
|
|
|
llvm::StringRef Result = A->getName();
|
|
if (not Result.empty()) {
|
|
return Result.str();
|
|
} else {
|
|
const llvm::Function *F = A->getParent();
|
|
return getName(F) + ":" + std::to_string(A->getArgNo());
|
|
}
|
|
}
|
|
|
|
/// \brief Return a sensible name for the given Value
|
|
/// \return if \p V is an Instruction, call the appropriate getName function,
|
|
/// otherwise return a pointer to \p V.
|
|
inline std::string getName(const llvm::Value *V) {
|
|
if (V != nullptr) {
|
|
if (auto *I = llvm::dyn_cast<llvm::Instruction>(V))
|
|
return getName(I);
|
|
if (auto *F = llvm::dyn_cast<llvm::Function>(V))
|
|
return getName(F);
|
|
if (auto *B = llvm::dyn_cast<llvm::BasicBlock>(V))
|
|
return getName(B);
|
|
if (auto *C = llvm::dyn_cast<llvm::ConstantInt>(V)) {
|
|
std::string Result;
|
|
llvm::raw_string_ostream S(Result);
|
|
C->print(S);
|
|
S.flush();
|
|
return Result;
|
|
}
|
|
if (auto *A = llvm::dyn_cast<llvm::Argument>(V))
|
|
return getName(A);
|
|
}
|
|
std::stringstream SS;
|
|
SS << "0x" << std::hex << intptr_t(V);
|
|
return SS.str();
|
|
}
|
|
|
|
inline llvm::BasicBlock *blockByName(llvm::Function *F, const char *Name) {
|
|
using namespace llvm;
|
|
|
|
for (BasicBlock &BB : *F)
|
|
if (BB.hasName() and BB.getName() == StringRef(Name))
|
|
return &BB;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
template<typename T>
|
|
concept DerivedFromLLVMValue = std::is_base_of_v<llvm::Value,
|
|
std::remove_const_t<T>>;
|
|
|
|
/// \brief Specialization of writeToLog for llvm::Value-derived types
|
|
template<DerivedFromLLVMValue T>
|
|
inline void writeToLog(Logger<true> &This, T *I, int) {
|
|
if (I != nullptr)
|
|
This << getName(I);
|
|
else
|
|
This << "nullptr";
|
|
}
|
|
|
|
inline llvm::LLVMContext &getContext(const llvm::Module *M) {
|
|
return M->getContext();
|
|
}
|
|
|
|
inline llvm::LLVMContext &getContext(const llvm::GlobalObject *G) {
|
|
return getContext(G->getParent());
|
|
}
|
|
|
|
inline llvm::LLVMContext &getContext(const llvm::BasicBlock *BB) {
|
|
return getContext(BB->getParent());
|
|
}
|
|
|
|
inline llvm::LLVMContext &getContext(const llvm::Instruction *I) {
|
|
return getContext(I->getParent());
|
|
}
|
|
|
|
inline llvm::LLVMContext &getContext(const llvm::Value *V) {
|
|
if (auto *I = llvm::dyn_cast<const llvm::Instruction>(V))
|
|
return getContext(I);
|
|
else if (auto *G = llvm::dyn_cast<const llvm::GlobalObject>(V))
|
|
return getContext(G);
|
|
else
|
|
revng_abort();
|
|
}
|
|
|
|
inline const llvm::Module *getModule(const llvm::Function *F) {
|
|
if (F == nullptr)
|
|
return nullptr;
|
|
return F->getParent();
|
|
}
|
|
|
|
inline const llvm::Module *getModule(const llvm::BasicBlock *BB) {
|
|
if (BB == nullptr)
|
|
return nullptr;
|
|
return getModule(BB->getParent());
|
|
}
|
|
|
|
inline const llvm::Module *getModule(const llvm::Instruction *I) {
|
|
if (I == nullptr)
|
|
return nullptr;
|
|
return getModule(I->getParent());
|
|
}
|
|
|
|
inline const llvm::Module *getModule(const llvm::Value *I) {
|
|
if (I == nullptr)
|
|
return nullptr;
|
|
return getModule(llvm::cast<const llvm::Instruction>(I));
|
|
}
|
|
|
|
inline llvm::Module *getModule(llvm::Function *F) {
|
|
if (F == nullptr)
|
|
return nullptr;
|
|
return F->getParent();
|
|
}
|
|
|
|
inline llvm::Module *getModule(llvm::BasicBlock *BB) {
|
|
if (BB == nullptr)
|
|
return nullptr;
|
|
return getModule(BB->getParent());
|
|
}
|
|
|
|
inline llvm::Module *getModule(llvm::Instruction *I) {
|
|
if (I == nullptr)
|
|
return nullptr;
|
|
return getModule(I->getParent());
|
|
}
|
|
|
|
inline llvm::Module *getModule(llvm::Value *I) {
|
|
if (I == nullptr)
|
|
return nullptr;
|
|
return getModule(llvm::cast<llvm::Instruction>(I));
|
|
}
|
|
|
|
/// \brief Helper class to easily create and use LLVM metadata
|
|
class QuickMetadata {
|
|
public:
|
|
QuickMetadata(llvm::LLVMContext &Context) :
|
|
C(Context),
|
|
Int32Ty(llvm::IntegerType::get(C, 32)),
|
|
Int64Ty(llvm::IntegerType::get(C, 64)) {}
|
|
|
|
llvm::MDString *get(const char *String) {
|
|
return llvm::MDString::get(C, String);
|
|
}
|
|
|
|
llvm::MDString *get(llvm::StringRef String) {
|
|
return llvm::MDString::get(C, String);
|
|
}
|
|
|
|
llvm::ConstantAsMetadata *get(const llvm::APInt &N) {
|
|
return llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(C, N));
|
|
}
|
|
|
|
llvm::ConstantAsMetadata *get(llvm::Constant *C) {
|
|
return llvm::ConstantAsMetadata::get(C);
|
|
}
|
|
|
|
llvm::ConstantAsMetadata *get(uint32_t Integer) {
|
|
auto *Constant = llvm::ConstantInt::get(Int32Ty, Integer);
|
|
return llvm::ConstantAsMetadata::get(Constant);
|
|
}
|
|
|
|
llvm::ConstantAsMetadata *get(uint64_t Integer) {
|
|
auto *Constant = llvm::ConstantInt::get(Int64Ty, Integer);
|
|
return llvm::ConstantAsMetadata::get(Constant);
|
|
}
|
|
|
|
llvm::ConstantAsMetadata *get(int32_t Integer) {
|
|
auto *Constant = llvm::ConstantInt::getSigned(Int32Ty, Integer);
|
|
return llvm::ConstantAsMetadata::get(Constant);
|
|
}
|
|
|
|
llvm::ConstantAsMetadata *get(int64_t Integer) {
|
|
auto *Constant = llvm::ConstantInt::getSigned(Int64Ty, Integer);
|
|
return llvm::ConstantAsMetadata::get(Constant);
|
|
}
|
|
|
|
llvm::MDNode *get() { return llvm::MDNode::get(C, {}); }
|
|
|
|
llvm::MDTuple *tuple(const char *String) { return tuple(get(String)); }
|
|
|
|
llvm::MDTuple *tuple(llvm::StringRef String) { return tuple(get(String)); }
|
|
|
|
llvm::MDTuple *tuple(uint32_t Integer) { return tuple(get(Integer)); }
|
|
|
|
llvm::MDTuple *tuple(uint64_t Integer) { return tuple(get(Integer)); }
|
|
|
|
llvm::MDTuple *tuple(int32_t Integer) { return tuple(get(Integer)); }
|
|
|
|
llvm::MDTuple *tuple(int64_t Integer) { return tuple(get(Integer)); }
|
|
|
|
llvm::MDTuple *tuple(llvm::ArrayRef<llvm::Metadata *> MDs) {
|
|
return llvm::MDTuple::get(C, MDs);
|
|
}
|
|
|
|
llvm::MDTuple *tuple() { return llvm::MDTuple::get(C, {}); }
|
|
|
|
template<typename T>
|
|
T extract(const llvm::MDTuple *Tuple, unsigned Index) {
|
|
return extract<T>(Tuple->getOperand(Index).get());
|
|
}
|
|
|
|
template<typename T>
|
|
T extract(const llvm::Metadata *MD) {
|
|
revng_abort();
|
|
}
|
|
|
|
template<typename T>
|
|
T extract(llvm::Metadata *MD) {
|
|
revng_abort();
|
|
}
|
|
|
|
private:
|
|
llvm::LLVMContext &C;
|
|
llvm::IntegerType *Int32Ty;
|
|
llvm::IntegerType *Int64Ty;
|
|
};
|
|
|
|
template<>
|
|
inline llvm::MDTuple *
|
|
QuickMetadata::extract<llvm::MDTuple *>(llvm::Metadata *MD) {
|
|
return llvm::cast<llvm::MDTuple>(MD);
|
|
}
|
|
|
|
template<>
|
|
inline llvm::Constant *
|
|
QuickMetadata::extract<llvm::Constant *>(const llvm::Metadata *MD) {
|
|
auto *C = llvm::cast<llvm::ConstantAsMetadata>(MD);
|
|
return C->getValue();
|
|
}
|
|
|
|
template<>
|
|
inline llvm::Constant *
|
|
QuickMetadata::extract<llvm::Constant *>(llvm::Metadata *MD) {
|
|
auto *C = llvm::cast<llvm::ConstantAsMetadata>(MD);
|
|
return C->getValue();
|
|
}
|
|
|
|
template<>
|
|
inline llvm::ConstantInt *
|
|
QuickMetadata::extract<llvm::ConstantInt *>(const llvm::Metadata *MD) {
|
|
auto *C = llvm::cast<llvm::ConstantAsMetadata>(MD);
|
|
return llvm::cast<llvm::ConstantInt>(C->getValue());
|
|
}
|
|
|
|
template<>
|
|
inline llvm::ConstantInt *
|
|
QuickMetadata::extract<llvm::ConstantInt *>(llvm::Metadata *MD) {
|
|
auto *C = llvm::cast<llvm::ConstantAsMetadata>(MD);
|
|
return llvm::cast<llvm::ConstantInt>(C->getValue());
|
|
}
|
|
|
|
template<>
|
|
inline uint32_t QuickMetadata::extract<uint32_t>(const llvm::Metadata *MD) {
|
|
auto *C = llvm::cast<llvm::ConstantAsMetadata>(MD);
|
|
return getLimitedValue(C->getValue());
|
|
}
|
|
|
|
template<>
|
|
inline uint32_t QuickMetadata::extract<uint32_t>(llvm::Metadata *MD) {
|
|
auto *C = llvm::cast<llvm::ConstantAsMetadata>(MD);
|
|
return getLimitedValue(C->getValue());
|
|
}
|
|
|
|
template<>
|
|
inline uint64_t QuickMetadata::extract<uint64_t>(const llvm::Metadata *MD) {
|
|
auto *C = llvm::cast<llvm::ConstantAsMetadata>(MD);
|
|
return getLimitedValue(C->getValue());
|
|
}
|
|
|
|
template<>
|
|
inline uint64_t QuickMetadata::extract<uint64_t>(llvm::Metadata *MD) {
|
|
auto *C = llvm::cast<llvm::ConstantAsMetadata>(MD);
|
|
return getLimitedValue(C->getValue());
|
|
}
|
|
|
|
template<>
|
|
inline int32_t QuickMetadata::extract<int32_t>(const llvm::Metadata *MD) {
|
|
auto *C = llvm::cast<llvm::ConstantAsMetadata>(MD);
|
|
return getSignedLimitedValue(C->getValue());
|
|
}
|
|
|
|
template<>
|
|
inline int32_t QuickMetadata::extract<int32_t>(llvm::Metadata *MD) {
|
|
auto *C = llvm::cast<llvm::ConstantAsMetadata>(MD);
|
|
return getSignedLimitedValue(C->getValue());
|
|
}
|
|
|
|
template<>
|
|
inline int64_t QuickMetadata::extract<int64_t>(const llvm::Metadata *MD) {
|
|
auto *C = llvm::cast<llvm::ConstantAsMetadata>(MD);
|
|
return getSignedLimitedValue(C->getValue());
|
|
}
|
|
|
|
template<>
|
|
inline int64_t QuickMetadata::extract<int64_t>(llvm::Metadata *MD) {
|
|
auto *C = llvm::cast<llvm::ConstantAsMetadata>(MD);
|
|
return getSignedLimitedValue(C->getValue());
|
|
}
|
|
|
|
template<>
|
|
inline llvm::StringRef
|
|
QuickMetadata::extract<llvm::StringRef>(const llvm::Metadata *MD) {
|
|
return llvm::cast<llvm::MDString>(MD)->getString();
|
|
}
|
|
|
|
template<>
|
|
inline llvm::StringRef
|
|
QuickMetadata::extract<llvm::StringRef>(llvm::Metadata *MD) {
|
|
return llvm::cast<llvm::MDString>(MD)->getString();
|
|
}
|
|
|
|
template<>
|
|
inline const llvm::MDString *
|
|
QuickMetadata::extract<const llvm::MDString *>(const llvm::Metadata *MD) {
|
|
return llvm::cast<llvm::MDString>(MD);
|
|
}
|
|
|
|
template<>
|
|
inline llvm::MDString *
|
|
QuickMetadata::extract<llvm::MDString *>(llvm::Metadata *MD) {
|
|
return llvm::cast<llvm::MDString>(MD);
|
|
}
|
|
|
|
/// \brief Return the instruction coming before \p I, or nullptr if it's the
|
|
/// first.
|
|
inline llvm::Instruction *getPrevious(llvm::Instruction *I) {
|
|
llvm::BasicBlock::reverse_iterator It(++I->getReverseIterator());
|
|
if (It == I->getParent()->rend())
|
|
return nullptr;
|
|
|
|
return &*It;
|
|
}
|
|
|
|
/// \brief Return the instruction coming after \p I, or nullptr if it's the
|
|
/// last.
|
|
inline llvm::Instruction *getNext(llvm::Instruction *I) {
|
|
llvm::BasicBlock::iterator It(I);
|
|
if (It == I->getParent()->end())
|
|
return nullptr;
|
|
|
|
It++;
|
|
return &*It;
|
|
}
|
|
|
|
/// \brief Check whether the instruction/basic block is the first in its
|
|
/// container or not
|
|
template<typename T>
|
|
inline bool isFirst(T *I) {
|
|
revng_assert(I != nullptr);
|
|
return I == &*I->getParent()->begin();
|
|
}
|
|
|
|
static std::array<unsigned, 3> CastOpcodes = {
|
|
llvm::Instruction::BitCast,
|
|
llvm::Instruction::PtrToInt,
|
|
llvm::Instruction::IntToPtr,
|
|
};
|
|
|
|
// \brief If \p V is a cast Instruction or a cast ConstantExpr, return its only
|
|
// operand (recursively)
|
|
inline const llvm::Value *skipCasts(const llvm::Value *V) {
|
|
using namespace llvm;
|
|
while (isa<CastInst>(V) or isa<IntToPtrInst>(V) or isa<PtrToIntInst>(V)
|
|
or (isa<ConstantExpr>(V)
|
|
and contains(CastOpcodes, cast<ConstantExpr>(V)->getOpcode())))
|
|
V = cast<User>(V)->getOperand(0);
|
|
return V;
|
|
}
|
|
|
|
// \brief If \p V is a cast Instruction or a cast ConstantExpr, return its only
|
|
// operand (recursively)
|
|
inline llvm::Value *skipCasts(llvm::Value *V) {
|
|
using namespace llvm;
|
|
while (isa<CastInst>(V) or isa<IntToPtrInst>(V) or isa<PtrToIntInst>(V)
|
|
or (isa<ConstantExpr>(V)
|
|
and contains(CastOpcodes, cast<ConstantExpr>(V)->getOpcode())))
|
|
V = cast<User>(V)->getOperand(0);
|
|
return V;
|
|
}
|
|
|
|
inline const llvm::Function *getCallee(const llvm::Instruction *I) {
|
|
revng_assert(I != nullptr);
|
|
|
|
using namespace llvm;
|
|
if (auto *Call = dyn_cast<CallInst>(I))
|
|
return llvm::dyn_cast<Function>(skipCasts(Call->getCalledOperand()));
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
inline llvm::Function *getCallee(llvm::Instruction *I) {
|
|
revng_assert(I != nullptr);
|
|
|
|
using namespace llvm;
|
|
if (auto *Call = dyn_cast<CallInst>(I))
|
|
return llvm::dyn_cast<Function>(skipCasts(Call->getCalledOperand()));
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
inline bool isCallTo(const llvm::Instruction *I, llvm::StringRef Name) {
|
|
revng_assert(I != nullptr);
|
|
const llvm::Function *Callee = getCallee(I);
|
|
return Callee != nullptr && Callee->getName() == Name;
|
|
}
|
|
|
|
inline bool isCallTo(const llvm::Instruction *I, llvm::Function *F) {
|
|
revng_assert(I != nullptr);
|
|
const llvm::Function *Callee = getCallee(I);
|
|
return Callee != nullptr && Callee == F;
|
|
}
|
|
|
|
inline bool isHelper(const llvm::Function *F) {
|
|
return FunctionTags::Helper.isTagOf(F);
|
|
}
|
|
|
|
inline const llvm::CallInst *getCallToHelper(const llvm::Instruction *I) {
|
|
revng_assert(I != nullptr);
|
|
const llvm::Function *Callee = getCallee(I);
|
|
if (Callee != nullptr && isHelper(Callee))
|
|
return llvm::cast<llvm::CallInst>(I);
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
inline llvm::CallInst *getCallToHelper(llvm::Instruction *I) {
|
|
revng_assert(I != nullptr);
|
|
const llvm::Function *Callee = getCallee(I);
|
|
if (Callee != nullptr && isHelper(Callee))
|
|
return llvm::cast<llvm::CallInst>(I);
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
/// \brief Is \p I a call to an helper function?
|
|
inline bool isCallToHelper(const llvm::Instruction *I) {
|
|
return getCallToHelper(I) != nullptr;
|
|
}
|
|
|
|
inline llvm::CallInst *getCallTo(llvm::Instruction *I, llvm::StringRef Name) {
|
|
if (isCallTo(I, Name))
|
|
return llvm::cast<llvm::CallInst>(I);
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
inline llvm::CallInst *getCallTo(llvm::Instruction *I, llvm::Function *F) {
|
|
if (isCallTo(I, F))
|
|
return llvm::cast<llvm::CallInst>(I);
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
inline const llvm::CallInst *
|
|
getCallTo(const llvm::Instruction *I, llvm::StringRef Name) {
|
|
if (isCallTo(I, Name))
|
|
return llvm::cast<llvm::CallInst>(I);
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
inline const llvm::CallInst *
|
|
getCallTo(const llvm::Instruction *I, llvm::Function *F) {
|
|
if (isCallTo(I, F))
|
|
return llvm::cast<llvm::CallInst>(I);
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
inline std::vector<llvm::GlobalVariable *>
|
|
extractCSVs(llvm::Instruction *Call, unsigned MDKindID) {
|
|
using namespace llvm;
|
|
|
|
std::vector<GlobalVariable *> Result;
|
|
auto *Tuple = cast_or_null<MDTuple>(Call->getMetadata(MDKindID));
|
|
if (Tuple == nullptr)
|
|
return Result;
|
|
|
|
QuickMetadata QMD(getContext(Call));
|
|
|
|
auto OperandsRange = QMD.extract<MDTuple *>(Tuple, 1)->operands();
|
|
for (const MDOperand &Operand : OperandsRange) {
|
|
auto *CSV = QMD.extract<Constant *>(Operand.get());
|
|
Result.push_back(cast<GlobalVariable>(CSV));
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
class CSVsUsage {
|
|
public:
|
|
void sort() {
|
|
std::sort(Read.begin(), Read.end());
|
|
std::sort(Written.begin(), Written.end());
|
|
}
|
|
|
|
public:
|
|
std::vector<llvm::GlobalVariable *> Read;
|
|
std::vector<llvm::GlobalVariable *> Written;
|
|
};
|
|
|
|
inline llvm::Optional<CSVsUsage>
|
|
getCSVUsedByHelperCallIfAvailable(llvm::Instruction *Call) {
|
|
revng_assert(isCallToHelper(Call));
|
|
|
|
const llvm::Module *M = getModule(Call);
|
|
const auto LoadMDKind = M->getMDKindID("revng.csvaccess.offsets.load");
|
|
const auto StoreMDKind = M->getMDKindID("revng.csvaccess.offsets.store");
|
|
|
|
if (Call->getMetadata(LoadMDKind) == nullptr
|
|
and Call->getMetadata(StoreMDKind) == nullptr) {
|
|
return {};
|
|
}
|
|
|
|
CSVsUsage Result;
|
|
Result.Read = extractCSVs(Call, LoadMDKind);
|
|
Result.Written = extractCSVs(Call, StoreMDKind);
|
|
return Result;
|
|
}
|
|
|
|
inline CSVsUsage getCSVUsedByHelperCall(llvm::Instruction *Call) {
|
|
return *getCSVUsedByHelperCallIfAvailable(Call);
|
|
}
|
|
|
|
inline MetaAddress getBasicBlockPC(llvm::BasicBlock *BB) {
|
|
using namespace llvm;
|
|
|
|
Instruction *I = BB->getFirstNonPHI();
|
|
if (I == nullptr)
|
|
return MetaAddress::invalid();
|
|
|
|
if (llvm::CallInst *Call = getCallTo(I, "newpc"))
|
|
return MetaAddress::fromConstant(Call->getOperand(0));
|
|
|
|
return MetaAddress::invalid();
|
|
}
|
|
|
|
inline MetaAddress getBasicBlockJumpTarget(llvm::BasicBlock *BB) {
|
|
using namespace llvm;
|
|
|
|
Instruction *I = BB->getFirstNonPHI();
|
|
if (I == nullptr)
|
|
return MetaAddress::invalid();
|
|
|
|
if (llvm::CallInst *Call = getCallTo(I, "newpc")) {
|
|
if (getLimitedValue(Call->getOperand(2)) == 1) {
|
|
return MetaAddress::fromConstant(Call->getOperand(0));
|
|
}
|
|
}
|
|
|
|
return MetaAddress::invalid();
|
|
}
|
|
|
|
template<typename V>
|
|
concept ValueLikePrintable = requires(V Val) {
|
|
Val.print(std::declval<llvm::raw_ostream &>(), true);
|
|
};
|
|
|
|
template<typename F>
|
|
concept ModFunLikePrintable = requires(F Fun) {
|
|
Fun.print(std::declval<llvm::raw_ostream &>(), nullptr, false, true);
|
|
};
|
|
|
|
// clang-format off
|
|
template<typename T>
|
|
concept LLVMRawOStreamPrintable = not ValueLikePrintable<T>
|
|
and not ModFunLikePrintable<T>
|
|
and requires(T TheT) {
|
|
TheT.print(std::declval<llvm::raw_ostream &>());
|
|
};
|
|
// clang-format on
|
|
|
|
// This is enabled only for references to types that inherit from llvm::Value
|
|
// but not from llvm::Function, since llvm::Function has a different prototype
|
|
// for the print() method
|
|
template<ValueLikePrintable ValueRef>
|
|
inline std::string dumpToString(ValueRef &V) {
|
|
std::string Result;
|
|
llvm::raw_string_ostream Stream(Result);
|
|
V.print(Stream, true);
|
|
Stream.flush();
|
|
return Result;
|
|
}
|
|
|
|
// This is enabled only for references to types that inherit from llvm::Module
|
|
// or from llvm::Function, which share the same prototype for the print() method
|
|
template<ModFunLikePrintable ModOrFunRef>
|
|
inline std::string dumpToString(ModOrFunRef &M) {
|
|
std::string Result;
|
|
llvm::raw_string_ostream Stream(Result);
|
|
M.print(Stream, nullptr, false, true);
|
|
Stream.flush();
|
|
return Result;
|
|
}
|
|
|
|
// This is enabled for all types with a print() method that prints to an
|
|
// llvm::raw_ostream
|
|
template<LLVMRawOStreamPrintable T>
|
|
inline std::string dumpToString(T &TheT) {
|
|
std::string Result;
|
|
llvm::raw_string_ostream Stream(Result);
|
|
TheT.print(Stream);
|
|
Stream.flush();
|
|
return Result;
|
|
}
|
|
|
|
// clang-format off
|
|
template<typename T>
|
|
concept LLVMRawOStreamDumpable = not ValueLikePrintable<T>
|
|
and not ModFunLikePrintable<T>
|
|
and requires(T TheT) {
|
|
TheT.dump(std::declval<llvm::raw_ostream &>());
|
|
};
|
|
// clang-format on
|
|
|
|
template<bool B, LLVMRawOStreamDumpable Dumpable>
|
|
inline void writeToLog(Logger<B> &L, const Dumpable &P, int /* Ignore */) {
|
|
if (L.isEnabled()) {
|
|
llvm::SmallString<32> Buffer;
|
|
{
|
|
llvm::raw_svector_ostream Stream(Buffer);
|
|
P.dump(Stream);
|
|
L << Stream.str().str();
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
requires std::is_pointer_v<T>
|
|
inline std::string dumpToString(T TheT) {
|
|
if (TheT == nullptr)
|
|
return "nullptr";
|
|
return dumpToString(*TheT);
|
|
}
|
|
|
|
void dumpModule(const llvm::Module *M, const char *Path) debug_function;
|
|
|
|
llvm::PointerType *getStringPtrType(llvm::LLVMContext &C);
|
|
|
|
llvm::GlobalVariable *
|
|
buildString(llvm::Module *M, llvm::StringRef String, const llvm::Twine &Name);
|
|
|
|
llvm::Constant *buildStringPtr(llvm::Module *M,
|
|
llvm::StringRef String,
|
|
const llvm::Twine &Name);
|
|
|
|
llvm::Constant *getUniqueString(llvm::Module *M,
|
|
llvm::StringRef Namespace,
|
|
llvm::StringRef String,
|
|
const llvm::Twine &Name = llvm::Twine());
|
|
|
|
llvm::StringRef extractFromConstantStringPtr(llvm::Value *V);
|
|
|
|
inline llvm::User *getUniqueUser(llvm::Value *V) {
|
|
llvm::User *Result = nullptr;
|
|
|
|
for (llvm::User *U : V->users()) {
|
|
if (Result != nullptr)
|
|
return nullptr;
|
|
else
|
|
Result = U;
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
/// \brief Find the first call to newpc starting from \p TheInstruction
|
|
///
|
|
llvm::CallInst *getLastNewPC(llvm::Instruction *TheInstruction);
|
|
|
|
/// \brief Find the PC which lead to generated \p TheInstruction
|
|
///
|
|
/// \return a pair of integers: the first element represents the PC and the
|
|
/// second the size of the instruction.
|
|
std::pair<MetaAddress, uint64_t> getPC(llvm::Instruction *TheInstruction);
|
|
|
|
/// \brief Replace all uses of \Old, with \New in \F.
|
|
///
|
|
/// \return true if it changes something, false otherwise.
|
|
inline bool replaceAllUsesInFunctionWith(llvm::Function *F,
|
|
llvm::Value *Old,
|
|
llvm::Value *New) {
|
|
using namespace llvm;
|
|
if (Old == New)
|
|
return false;
|
|
|
|
bool Changed = false;
|
|
|
|
SmallPtrSet<ConstantExpr *, 8> OldUserConstExprs;
|
|
auto UI = Old->use_begin();
|
|
auto E = Old->use_end();
|
|
while (UI != E) {
|
|
Use &U = *UI;
|
|
++UI;
|
|
|
|
if (auto *I = dyn_cast<Instruction>(U.getUser())) {
|
|
if (I->getFunction() == F) {
|
|
U.set(New);
|
|
Changed = true;
|
|
}
|
|
} else if (auto *CE = dyn_cast<ConstantExpr>(U.getUser())) {
|
|
// We can't convert ConstantExprs to Instructions while iterating on Old
|
|
// uses. This would create new uses of Old (the new Instructions generated
|
|
// by converting the ConstantExprs to Instructions) while iterating on Old
|
|
// uses, so the trick with pre-incrementing the iterators used above would
|
|
// not be enough to guard us from iterator invalidation.
|
|
// We store ConstantExpr uses in a helper vector and process them later.
|
|
if (CE->isCast())
|
|
OldUserConstExprs.insert(CE);
|
|
}
|
|
}
|
|
|
|
// Iterate on all ConstantExpr that use Old.
|
|
for (ConstantExpr *OldUserCE : OldUserConstExprs) {
|
|
// For each ConstantExpr that uses Old, we are interested in its uses in F,
|
|
// so we iterate on all uses of OldUserCE, looking for uses in Instructions
|
|
// that are in F.
|
|
// When we find one, we cannot directly substitute the use of Old in
|
|
// OldUserCE, because that is a constant expression that might be used
|
|
// somewhere else, possibly outside of F.
|
|
// What we do instead is to create an Instruction in F that is equivalent to
|
|
// OldUserCE, and substitute Old with New only in that instruction.
|
|
auto CEIt = OldUserCE->use_begin();
|
|
auto CEEnd = OldUserCE->use_end();
|
|
for (; CEIt != CEEnd;) {
|
|
Use &CEUse = *CEIt;
|
|
++CEIt;
|
|
auto *CEInstrUser = dyn_cast<Instruction>(CEUse.getUser());
|
|
if (CEInstrUser and CEInstrUser->getFunction() == F) {
|
|
Instruction *CastInst = OldUserCE->getAsInstruction();
|
|
CastInst->replaceUsesOfWith(Old, New);
|
|
CastInst->insertBefore(CEInstrUser);
|
|
CEUse.set(CastInst);
|
|
Changed = true;
|
|
}
|
|
}
|
|
}
|
|
return Changed;
|
|
}
|
|
|
|
/// \brief Checks if \p I is a marker
|
|
///
|
|
/// A marker a function call to an empty function acting as meta-information,
|
|
/// for example the `function_call` marker.
|
|
inline bool isMarker(const llvm::Instruction *I) {
|
|
if (auto *Callee = getCallee(I))
|
|
return FunctionTags::Marker.isTagOf(Callee);
|
|
|
|
return false;
|
|
}
|
|
|
|
inline llvm::Instruction *nextNonMarker(llvm::Instruction *I) {
|
|
auto It = I->getIterator();
|
|
auto End = I->getParent()->end();
|
|
do {
|
|
It++;
|
|
revng_assert(It != End);
|
|
} while (isMarker(&*It));
|
|
|
|
revng_assert(It != End);
|
|
return &*It;
|
|
}
|
|
|
|
/// \brief Return the call to the marker function_call
|
|
/// if \p T is a function call in the input assembly.
|
|
inline llvm::CallInst *getFunctionCall(llvm::Instruction *T) {
|
|
revng_assert(T && T->isTerminator());
|
|
llvm::Instruction *Previous = getPrevious(T);
|
|
while (Previous != nullptr
|
|
&& (isMarker(Previous) || isCallTo(Previous, "abort"))) {
|
|
if (auto *Call = getCallTo(Previous, "function_call"))
|
|
return Call;
|
|
|
|
Previous = getPrevious(Previous);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
inline llvm::CallInst *getFunctionCall(llvm::BasicBlock *BB) {
|
|
return getFunctionCall(BB->getTerminator());
|
|
}
|
|
|
|
/// \brief Return true if \p T is a function call in the input assembly.
|
|
inline bool isFunctionCall(llvm::Instruction *T) {
|
|
return getFunctionCall(T) != nullptr;
|
|
}
|
|
|
|
inline bool isFunctionCall(llvm::BasicBlock *BB) {
|
|
return isFunctionCall(BB->getTerminator());
|
|
}
|
|
|
|
/// \brief Return the callee basic block given a function_call marker.
|
|
inline llvm::BasicBlock *getFunctionCallCallee(llvm::Instruction *T) {
|
|
if (auto *Call = getFunctionCall(T)) {
|
|
if (auto *Callee = llvm::dyn_cast<llvm::BlockAddress>(Call->getOperand(0)))
|
|
return Callee->getBasicBlock();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
inline llvm::BasicBlock *getFunctionCallCallee(llvm::BasicBlock *BB) {
|
|
return getFunctionCallCallee(BB->getTerminator());
|
|
}
|
|
|
|
/// \brief Return the fall-through basic block given a function_call marker.
|
|
inline llvm::BasicBlock *getFallthrough(llvm::Instruction *T) {
|
|
if (auto *Call = getFunctionCall(T)) {
|
|
auto *Fallthrough = llvm::cast<llvm::BlockAddress>(Call->getOperand(1));
|
|
return Fallthrough->getBasicBlock();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
inline llvm::BasicBlock *getFallthrough(llvm::BasicBlock *BB) {
|
|
return getFallthrough(BB->getTerminator());
|
|
}
|
|
|
|
/// \brief Return true if \p T is has a fallthrough basic block.
|
|
inline bool isFallthrough(llvm::Instruction *T) {
|
|
return getFallthrough(T) != nullptr;
|
|
}
|
|
|
|
inline bool isFallthrough(llvm::BasicBlock *BB) {
|
|
return isFallthrough(BB->getTerminator());
|
|
}
|
|
|
|
template<typename T>
|
|
inline llvm::Type *cTypeToLLVMType(llvm::LLVMContext &C) {
|
|
using namespace std;
|
|
using namespace llvm;
|
|
if constexpr (is_integral_v<T>) {
|
|
return Type::getIntNTy(C, 8 * sizeof(T));
|
|
} else if (is_pointer_v<T>) {
|
|
return cTypeToLLVMType<remove_pointer_t<T>>(C)->getPointerTo();
|
|
} else if (is_void_v<T>) {
|
|
return Type::getVoidTy(C);
|
|
} else {
|
|
revng_abort();
|
|
}
|
|
}
|
|
|
|
template<typename ReturnT, typename... Args>
|
|
inline llvm::FunctionType *
|
|
createFunctionType(llvm::LLVMContext &C, bool Variadic = false) {
|
|
return llvm::FunctionType::get(cTypeToLLVMType<ReturnT>(C),
|
|
{ cTypeToLLVMType<Args>(C)... },
|
|
Variadic);
|
|
}
|
|
|
|
inline cppcoro::generator<llvm::CallBase *> callers(llvm::Function *F) {
|
|
using namespace llvm;
|
|
SmallVector<Value *, 8> Queue;
|
|
Queue.push_back(F);
|
|
|
|
while (not Queue.empty()) {
|
|
Value *V = Queue.back();
|
|
Queue.pop_back();
|
|
|
|
for (User *U : V->users()) {
|
|
if (auto *Call = dyn_cast<CallBase>(U)) {
|
|
co_yield Call;
|
|
} else if (auto *CE = dyn_cast<ConstantExpr>(U)) {
|
|
if (CE->isCast())
|
|
Queue.push_back(CE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
concept HasMetadata = requires(T &Value,
|
|
const T &ConstValue,
|
|
llvm::StringRef KindName,
|
|
unsigned KindID,
|
|
llvm::MDNode *MD) {
|
|
Value.setMetadata(KindName, MD);
|
|
Value.setMetadata(KindID, MD);
|
|
{ ConstValue.getMetadata(KindName) } -> std::same_as<llvm::MDNode *>;
|
|
{ ConstValue.getMetadata(KindID) } -> std::same_as<llvm::MDNode *>;
|
|
};
|
|
|
|
static_assert(HasMetadata<llvm::Instruction>);
|
|
static_assert(HasMetadata<llvm::Function>);
|
|
static_assert(HasMetadata<llvm::GlobalVariable>);
|
|
static_assert(not HasMetadata<llvm::Constant>);
|
|
|
|
template<HasMetadata T>
|
|
MetaAddress getMetaAddressMetadata(const T *U, llvm::StringRef Name) {
|
|
using namespace llvm;
|
|
|
|
if (auto *MD = dyn_cast_or_null<MDTuple>(U->getMetadata(Name)))
|
|
if (auto *VAM = dyn_cast<ValueAsMetadata>(MD->getOperand(0)))
|
|
return MetaAddress::fromConstant(VAM->getValue());
|
|
|
|
return MetaAddress::invalid();
|
|
}
|
|
|
|
template<typename T>
|
|
inline llvm::cl::opt<T> *
|
|
getOption(llvm::StringMap<llvm::cl::Option *> &Options, const char *Name) {
|
|
return static_cast<llvm::cl::opt<T> *>(Options[Name]);
|
|
}
|
|
|
|
/// Extract MD text from MDString or GlobalVariable
|
|
llvm::StringRef getText(const llvm::Instruction *I, unsigned Kind);
|
|
|
|
template<typename T, typename Inserter>
|
|
inline void
|
|
setInsertPointToFirstNonAlloca(llvm::IRBuilder<T, Inserter> &Builder,
|
|
llvm::Function &F) {
|
|
using namespace llvm;
|
|
|
|
BasicBlock &Entry = F.getEntryBlock();
|
|
for (Instruction &I : Entry) {
|
|
if (not isa<AllocaInst>(&I)) {
|
|
Builder.SetInsertPoint(&I);
|
|
return;
|
|
}
|
|
}
|
|
revng_abort();
|
|
}
|
|
|
|
inline llvm::Value *getPointer(llvm::User *U) {
|
|
using namespace llvm;
|
|
|
|
if (auto *Load = dyn_cast<LoadInst>(U))
|
|
return Load->getPointerOperand();
|
|
else if (auto *Store = dyn_cast<StoreInst>(U))
|
|
return Store->getPointerOperand();
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
inline unsigned getPointeeSize(llvm::Value *Pointer) {
|
|
using namespace llvm;
|
|
|
|
revng_assert(Pointer->getType()->isPointerTy());
|
|
Type *Pointee = Pointer->getType()->getPointerElementType();
|
|
unsigned Size = Pointee->getIntegerBitWidth();
|
|
revng_assert(Size % 8 == 0);
|
|
return Pointer->getType()->getPointerElementType()->getIntegerBitWidth() / 8;
|
|
}
|
|
|
|
inline unsigned getMemoryAccessSize(llvm::Instruction *I) {
|
|
return getPointeeSize(getPointer(I));
|
|
}
|
|
|
|
inline llvm::StringRef getDynamicSymbol(llvm::BasicBlock *BB) {
|
|
auto *NewPCCall = getCallTo(&*BB->begin(), "newpc");
|
|
revng_assert(NewPCCall != nullptr);
|
|
auto *SymbolNameValue = NewPCCall->getArgOperand(4);
|
|
if (isa<llvm::ConstantPointerNull>(SymbolNameValue))
|
|
return {};
|
|
return extractFromConstantStringPtr(SymbolNameValue);
|
|
}
|
|
|
|
/// Adds NewArguments and changes the return type of \p OldFunction
|
|
///
|
|
/// \param OldFunction the original function from which the body will be stolen.
|
|
/// \param NewReturnType the new return type. It can be: 1) nullptr to preserve
|
|
/// the old one, 2) the old type or 3), if the original type is void, a
|
|
/// new type.
|
|
/// \param NewArguments extra arguments to add on top of the existing ones.
|
|
///
|
|
/// \return the newly created Function.
|
|
///
|
|
/// \note \p OldFunction will not be deleted or RAUW'd.
|
|
llvm::Function *changeFunctionType(llvm::Function &OldFunction,
|
|
llvm::Type *NewReturnType,
|
|
llvm::ArrayRef<llvm::Type *> NewArguments);
|