mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
296 lines
9.5 KiB
C++
296 lines
9.5 KiB
C++
#pragma once
|
|
|
|
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/IR/Value.h"
|
|
|
|
#include "revng/Model/Architecture.h"
|
|
#include "revng/Support/BlockType.h"
|
|
#include "revng/Support/IRBuilder.h"
|
|
#include "revng/Support/IRHelpers.h"
|
|
|
|
inline llvm::IntegerType *getCSVType(llvm::GlobalVariable *CSV) {
|
|
using namespace llvm;
|
|
return cast<IntegerType>(CSV->getValueType());
|
|
}
|
|
|
|
namespace NextJumpTarget {
|
|
|
|
enum Values {
|
|
Unique,
|
|
Multiple,
|
|
Helper
|
|
};
|
|
|
|
}; // namespace NextJumpTarget
|
|
|
|
namespace PCAffectingCSV {
|
|
|
|
enum Values {
|
|
PC,
|
|
IsThumb
|
|
};
|
|
|
|
}; // namespace PCAffectingCSV
|
|
|
|
class ProgramCounterHandler {
|
|
protected:
|
|
using CSVFactory = llvm::function_ref<
|
|
llvm::GlobalVariable *(PCAffectingCSV::Values CSVID)>;
|
|
|
|
static constexpr const char *AddressSpaceName = "pc_address_space";
|
|
static constexpr const char *EpochName = "pc_epoch";
|
|
static constexpr const char *TypeName = "pc_type";
|
|
|
|
protected:
|
|
uint64_t Alignment = 0;
|
|
llvm::GlobalVariable *AddressCSV = nullptr;
|
|
llvm::GlobalVariable *EpochCSV = nullptr;
|
|
llvm::GlobalVariable *AddressSpaceCSV = nullptr;
|
|
llvm::GlobalVariable *TypeCSV = nullptr;
|
|
|
|
std::set<llvm::GlobalVariable *> CSVsAffectingPC;
|
|
|
|
public:
|
|
using DispatcherTarget = std::pair<MetaAddress, llvm::BasicBlock *>;
|
|
using DispatcherTargets = std::vector<DispatcherTarget>;
|
|
|
|
protected:
|
|
ProgramCounterHandler(unsigned Alignment) : Alignment(Alignment) {}
|
|
|
|
public:
|
|
virtual ~ProgramCounterHandler() {}
|
|
|
|
public:
|
|
static std::unique_ptr<ProgramCounterHandler>
|
|
create(model::Architecture::Values Architecture,
|
|
llvm::Module *M,
|
|
CSVFactory Factory);
|
|
|
|
static std::unique_ptr<ProgramCounterHandler>
|
|
fromModule(model::Architecture::Values Architecture, llvm::Module *M);
|
|
|
|
public:
|
|
std::array<llvm::GlobalVariable *, 4> pcCSVs() const {
|
|
return { EpochCSV, AddressSpaceCSV, TypeCSV, AddressCSV };
|
|
}
|
|
|
|
/// Hook for the emission of a store to a CSV
|
|
///
|
|
/// \param Builder Builder to employ in order to inject new instructions.
|
|
/// Those are positioned after \p Store
|
|
/// \param Store the StoreInst targeting a CSV
|
|
///
|
|
/// \return true if new instructions have been emitted.
|
|
bool handleStore(revng::IRBuilder &Builder, llvm::StoreInst *Store) const {
|
|
if (affectsPC(Store))
|
|
return handleStoreInternal(Builder, Store);
|
|
return false;
|
|
}
|
|
|
|
void initializePC(revng::IRBuilder &Builder, MetaAddress NewPC) const {
|
|
setPC(Builder, NewPC);
|
|
initializePCInternal(Builder, NewPC);
|
|
}
|
|
|
|
void setPC(revng::IRBuilder &Builder, MetaAddress NewPC) const {
|
|
revng_assert(NewPC.isValid() and NewPC.isCode());
|
|
store(Builder, AddressCSV, NewPC.address());
|
|
store(Builder, EpochCSV, NewPC.epoch());
|
|
store(Builder, AddressSpaceCSV, NewPC.addressSpace());
|
|
store(Builder, TypeCSV, NewPC.type());
|
|
}
|
|
|
|
void expandNewPC(llvm::CallBase *Call) const {
|
|
revng_assert(isCallTo(Call, "newpc"));
|
|
MetaAddress Address = addressFromNewPC(Call);
|
|
revng::NonDebugInfoCheckingIRBuilder Builder(Call);
|
|
setPC(Builder, Address);
|
|
}
|
|
|
|
void setCurrentPCPlainMetaAddress(revng::IRBuilder &Builder) const;
|
|
void setLastPCPlainMetaAddress(revng::IRBuilder &Builder,
|
|
const MetaAddress &Address) const;
|
|
void setPlainMetaAddress(revng::IRBuilder &Builder,
|
|
llvm::StringRef GlobalName,
|
|
const MetaAddress &Address) const;
|
|
|
|
protected:
|
|
virtual void initializePCInternal(revng::IRBuilder &Builder,
|
|
MetaAddress NewPC) const = 0;
|
|
|
|
virtual bool handleStoreInternal(revng::IRBuilder &Builder,
|
|
llvm::StoreInst *Store) const = 0;
|
|
|
|
public:
|
|
bool affectsPC(llvm::GlobalVariable *GV) const {
|
|
return CSVsAffectingPC.contains(GV);
|
|
}
|
|
|
|
bool affectsPC(llvm::StoreInst *Store) const {
|
|
using namespace llvm;
|
|
if (auto *CSV = dyn_cast<GlobalVariable>(Store->getPointerOperand()))
|
|
return affectsPC(CSV);
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool isPCAffectingHelper(llvm::Instruction *I) const;
|
|
|
|
/// \return an empty optional if the PC has not changed on at least one path,
|
|
/// an invalid MetaAddress in case there isn't a single next PC, or,
|
|
/// finally, a valid MetaAddress representing the only possible next
|
|
/// PC
|
|
std::pair<NextJumpTarget::Values, MetaAddress>
|
|
getUniqueJumpTarget(llvm::BasicBlock *BB);
|
|
|
|
void deserializePC(revng::IRBuilder &Builder) const {
|
|
using namespace llvm;
|
|
|
|
// Load and re-store each CSV affecting the PC and then feed them to
|
|
// handleStore
|
|
for (GlobalVariable *CSVAffectingPC : CSVsAffectingPC) {
|
|
auto *FakeLoad = Builder.createLoad(CSVAffectingPC);
|
|
auto *FakeStore = Builder.CreateStore(FakeLoad, CSVAffectingPC);
|
|
bool HasInjectedCode = handleStore(Builder, FakeStore);
|
|
eraseFromParent(FakeStore);
|
|
|
|
if (not HasInjectedCode) {
|
|
// The store did not produce any effect, the load is useless too
|
|
revng_assert(FakeLoad->use_begin() == FakeLoad->use_end());
|
|
eraseFromParent(FakeLoad);
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual llvm::Value *loadJumpablePC(revng::IRBuilder &Builder) const = 0;
|
|
|
|
virtual std::array<llvm::Value *, 4>
|
|
dissectJumpablePC(revng::IRBuilder &Builder,
|
|
llvm::Value *ToDissect,
|
|
model::Architecture::Values Arch) const = 0;
|
|
|
|
virtual void
|
|
deserializePCFromSignalContext(revng::IRBuilder &Builder,
|
|
llvm::Value *PCAddress,
|
|
llvm::Value *SavedRegisters) const = 0;
|
|
|
|
llvm::Instruction *composeIntegerPC(revng::IRBuilder &B) const {
|
|
return MetaAddress::composeIntegerPC(B,
|
|
align(B, B.createLoad(AddressCSV)),
|
|
B.createLoad(EpochCSV),
|
|
B.createLoad(AddressSpaceCSV),
|
|
B.createLoad(TypeCSV));
|
|
}
|
|
|
|
bool isPCSizedType(llvm::Type *T) const {
|
|
return T == AddressCSV->getValueType();
|
|
}
|
|
|
|
public:
|
|
struct DispatcherInfo {
|
|
llvm::SmallVector<llvm::BasicBlock *, 4> NewBlocks;
|
|
llvm::SwitchInst *Switch;
|
|
};
|
|
|
|
/// \param Targets the targets to materialize for the dispatcher. Will be
|
|
/// sorted.
|
|
DispatcherInfo
|
|
buildDispatcher(DispatcherTargets &Targets,
|
|
revng::IRBuilder &Builder,
|
|
llvm::BasicBlock *Default,
|
|
std::optional<BlockType::Values> SetBlockType) const;
|
|
|
|
DispatcherInfo
|
|
buildDispatcher(DispatcherTargets &Targets,
|
|
llvm::BasicBlock *CreateIn,
|
|
llvm::BasicBlock *Default,
|
|
std::optional<BlockType::Values> SetBlockType) const {
|
|
revng::NonDebugInfoCheckingIRBuilder Builder(CreateIn);
|
|
return buildDispatcher(Targets, Builder, Default, SetBlockType);
|
|
}
|
|
|
|
/// \note \p Root must not already contain a case for \p NewTarget
|
|
void addCaseToDispatcher(llvm::SwitchInst *Root,
|
|
const DispatcherTarget &NewTarget,
|
|
std::optional<BlockType::Values> SetBlockType) const;
|
|
|
|
void destroyDispatcher(llvm::SwitchInst *Root) const;
|
|
|
|
void buildHotPath(revng::IRBuilder &Builder,
|
|
const DispatcherTarget &CandidateTarget,
|
|
llvm::BasicBlock *Default) const;
|
|
|
|
protected:
|
|
void createMissingVariables(llvm::Module *M) {
|
|
if (EpochCSV == nullptr)
|
|
EpochCSV = createEpoch(M);
|
|
if (AddressSpaceCSV == nullptr)
|
|
AddressSpaceCSV = createAddressSpace(M);
|
|
if (TypeCSV == nullptr)
|
|
TypeCSV = createType(M);
|
|
}
|
|
|
|
llvm::Value *align(revng::IRBuilder &Builder, llvm::Value *V) const {
|
|
revng_assert(Alignment != 0);
|
|
|
|
if (Alignment == 1)
|
|
return V;
|
|
|
|
using namespace llvm;
|
|
revng_assert(isPowerOf2_64(Alignment));
|
|
auto *Type = cast<IntegerType>(V->getType());
|
|
Value *Mask = ConstantInt::get(Type, ~(Alignment - 1));
|
|
return Builder.CreateAnd(V, Mask);
|
|
}
|
|
|
|
protected:
|
|
void setMissingVariables(llvm::Module *M, llvm::StringRef AddressName) {
|
|
AddressCSV = M->getGlobalVariable(AddressName, true);
|
|
EpochCSV = M->getGlobalVariable(EpochName, true);
|
|
AddressSpaceCSV = M->getGlobalVariable(AddressSpaceName, true);
|
|
TypeCSV = M->getGlobalVariable(TypeName, true);
|
|
|
|
revng_assert(AddressCSV != nullptr and EpochCSV != nullptr
|
|
and AddressSpaceCSV != nullptr and TypeCSV != nullptr);
|
|
}
|
|
|
|
private:
|
|
static llvm::GlobalVariable *createEpoch(llvm::Module *M) {
|
|
return createVariable(M, EpochName, sizeof(MetaAddress::Epoch));
|
|
}
|
|
|
|
static llvm::GlobalVariable *createAddressSpace(llvm::Module *M) {
|
|
return createVariable(M,
|
|
AddressSpaceName,
|
|
sizeof(MetaAddress::AddressSpace));
|
|
}
|
|
|
|
static llvm::GlobalVariable *createType(llvm::Module *M) {
|
|
return createVariable(M, TypeName, sizeof(MetaAddress::Type));
|
|
}
|
|
|
|
static llvm::GlobalVariable *
|
|
createVariable(llvm::Module *M, llvm::StringRef Name, size_t Size) {
|
|
using namespace llvm;
|
|
auto *T = Type::getIntNTy(M->getContext(), Size * 8);
|
|
return new GlobalVariable(*M,
|
|
T,
|
|
false,
|
|
GlobalValue::ExternalLinkage,
|
|
ConstantInt::get(T, 0),
|
|
Name);
|
|
}
|
|
|
|
protected:
|
|
static llvm::StoreInst *
|
|
store(revng::IRBuilder &Builder, llvm::GlobalVariable *GV, uint64_t Value) {
|
|
using namespace llvm;
|
|
auto *Type = cast<IntegerType>(GV->getValueType());
|
|
return Builder.CreateStore(ConstantInt::get(Type, Value), GV);
|
|
}
|
|
};
|