mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
f8bd4c3bac
* Make the following private headers public:
* Lift/CPUStateAccessAnalysisPass.h
* Lift/CSVOffsets.h
* Lift/PTCDump.h
* Lift/VariableManager.h
* Move from revngSupport to revngLift:
* IRAnnotators.{h,cpp}
* SelfReferencingDbgAnnotationWriter.{h,cpp}
* Move from revngSupport to revngModel:
* FunctionTags.{h,cpp}
* ProgramCounterHandler.{h,cpp}
* Move from revngSupport to revngRecompile:
* OriginalAssemblyAnnotationWriter.{h,cpp}
308 lines
12 KiB
C++
308 lines
12 KiB
C++
#pragma once
|
|
|
|
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
#include "revng/Model/FunctionTags.h"
|
|
#include "revng/Support/OpaqueFunctionsPool.h"
|
|
|
|
namespace llvm {
|
|
|
|
class AllocaInst;
|
|
class CallInst;
|
|
class Function;
|
|
class Instruction;
|
|
class IntegerType;
|
|
class LoadInst;
|
|
class Module;
|
|
class StoreInst;
|
|
class Type;
|
|
class Value;
|
|
|
|
} // end namespace llvm
|
|
|
|
namespace model {
|
|
|
|
class Binary;
|
|
class Type;
|
|
|
|
} // end namespace model
|
|
|
|
/// Various LLVM Passes in the decompilation pipelines need to create local
|
|
/// variables and read/write memory accesses from/to them. In the legacy
|
|
/// decompilation pipeline these were represented by dedicated functions, tagged
|
|
/// with specific FunctionTags, to represent dedicated opcodes without using
|
|
/// LLVM intrinsics. This workaround with FunctionTags and custom opcodes is
|
|
/// scheduled to be dropped for the clift-based decompilation pipeline based on
|
|
/// the Clift MLIR dialect, which will use regular LLVM alloca/load/store
|
|
/// instructions.
|
|
///
|
|
/// This class provides a bunch of helpers to deal with creation of local
|
|
/// variables. The IsLegacy field is used to select at compile-time the
|
|
/// appropriate mode of operation:
|
|
/// - IsLegacy == true: uses the old FunctionTags and dedicated functions to
|
|
/// represent dedicated opcodes
|
|
/// - IsLegacy == false: uses regular LLVM alloca/load/store instructions
|
|
///
|
|
/// TODO: when the migration is over, the IsLegacy field can be dropped to
|
|
/// fully embrace the new ways.
|
|
template<bool IsLegacy>
|
|
class LocalVariableBuilder {
|
|
public:
|
|
using TypePair = FunctionTags::TypePair;
|
|
|
|
using AllocaInst = llvm::AllocaInst;
|
|
using CallInst = llvm::CallInst;
|
|
using LoadInst = llvm::LoadInst;
|
|
using StoreInst = llvm::StoreInst;
|
|
using Value = llvm::Value;
|
|
|
|
using AssignType = std::conditional_t<IsLegacy, CallInst, StoreInst>;
|
|
using CopyType = std::conditional_t<IsLegacy, CallInst, LoadInst>;
|
|
using LocalVarType = std::conditional_t<IsLegacy, CallInst, AllocaInst>;
|
|
using ReferenceType = std::conditional_t<IsLegacy, CallInst, Value>;
|
|
|
|
private:
|
|
const model::Binary &Binary;
|
|
|
|
/// The module that this class manipulates.
|
|
llvm::Module &M;
|
|
|
|
/// An LLVM integer type whose size matches the size of a pointer in the
|
|
/// Binary we're decompiling.
|
|
llvm::IntegerType *InputPointerSizedInteger = nullptr;
|
|
|
|
/// An LLVM integer type whose size matches the size of a pointer in the
|
|
/// target architecture
|
|
llvm::IntegerType *TargetPointerSizedInteger = nullptr;
|
|
|
|
/// An LLVM 8-bits integer
|
|
llvm::IntegerType *Int8Ty = nullptr;
|
|
|
|
/// The function where this helper inserts local variables.
|
|
llvm::Function *F = nullptr;
|
|
|
|
/// \name Data members used only for Legacy mode.
|
|
///
|
|
/// TODO: drop these when we drop legacy mode
|
|
///
|
|
///@{
|
|
|
|
/// Pool of functions that represent allocation of local variables
|
|
OpaqueFunctionsPool<llvm::Type *> LocalVarPool;
|
|
|
|
/// Pool of functions that represent the operation that assigns a value to a
|
|
/// local variables
|
|
OpaqueFunctionsPool<llvm::Type *> AssignPool;
|
|
|
|
/// Pool of functions that represent the operation that copies a value from a
|
|
/// local variables and returns a temporary
|
|
OpaqueFunctionsPool<llvm::Type *> CopyPool;
|
|
|
|
/// Pool of functions that represent the AddressOf operator.
|
|
/// This is a pointer to a pool, because users of of LocalVariableBuilders
|
|
/// typically want to inject other AddressOf operations, which means that they
|
|
/// need to have a shared pool of AddressOf, in order not to go out of sync.
|
|
OpaqueFunctionsPool<TypePair> &AddressOfPool;
|
|
|
|
/// LLVM Function used to represent the allocation of the stack frame.
|
|
llvm::Function *StackFrameAllocator = nullptr;
|
|
|
|
/// LLVM Function used to represent the allocation of the stack arguments for
|
|
/// a call to an isolated function.
|
|
llvm::Function *CallStackArgumentsAllocator = nullptr;
|
|
|
|
///@}
|
|
|
|
private:
|
|
LocalVariableBuilder(const model::Binary &TheBinary,
|
|
llvm::Module &TheModule,
|
|
OpaqueFunctionsPool<TypePair> *TheAddressOfPool);
|
|
|
|
LocalVariableBuilder(const model::Binary &TheBinary,
|
|
llvm::Module &TheModule) :
|
|
LocalVariableBuilder(TheBinary, TheModule, nullptr) {}
|
|
|
|
public:
|
|
~LocalVariableBuilder() = default;
|
|
|
|
LocalVariableBuilder(const LocalVariableBuilder &) = default;
|
|
|
|
LocalVariableBuilder(LocalVariableBuilder &&) = default;
|
|
|
|
bool isLegacy() const { return IsLegacy; }
|
|
|
|
public:
|
|
/// Factories to create LocalVariableBuilders
|
|
///
|
|
/// TODO: drop these when we drop legacy mode, in favor of just using the only
|
|
/// constructor left.
|
|
///
|
|
///@{
|
|
|
|
static LocalVariableBuilder
|
|
makeLegacyStackBuilder(const model::Binary &TheBinary,
|
|
llvm::Module &TheModule,
|
|
OpaqueFunctionsPool<TypePair> &TheAddressOfPool)
|
|
requires IsLegacy
|
|
{
|
|
return LocalVariableBuilder<IsLegacy>(TheBinary,
|
|
TheModule,
|
|
&TheAddressOfPool);
|
|
}
|
|
|
|
static LocalVariableBuilder make(const model::Binary &TheBinary,
|
|
llvm::Module &TheModule) {
|
|
return LocalVariableBuilder<IsLegacy>(TheBinary, TheModule);
|
|
}
|
|
|
|
static LocalVariableBuilder make(const model::Binary &TheBinary,
|
|
llvm::Function &TheFunction) {
|
|
LocalVariableBuilder<IsLegacy> LVB(TheBinary, *TheFunction.getParent());
|
|
LVB.setTargetFunction(&TheFunction);
|
|
return LVB;
|
|
}
|
|
|
|
///@}
|
|
|
|
public:
|
|
/// Sets the function where the LocalVariableBuilder injects instructions
|
|
/// representing local variables.
|
|
void setTargetFunction(llvm::Function *NewF) { F = NewF; }
|
|
|
|
/// Creates an llvm::Instruction that models the allocation of a local
|
|
/// variable.
|
|
/// The created instruction is inserted at the beginning of the function F.
|
|
/// This is typically an alloca, but it's a call to LocalVariable in legacy
|
|
/// mode.
|
|
/// TODO: this method can become const when we drop legacy mode.
|
|
LocalVarType *createLocalVariable(const model::Type &VariableType);
|
|
|
|
/// Creates an llvm::Instruction that models the allocation of a local
|
|
/// variable, and takes its address.
|
|
/// The created instruction is inserted at the beginning of the function F.
|
|
/// This is typically an alloca, but it's a call to LocalVariable in legacy
|
|
/// mode.
|
|
///
|
|
/// In legacy mode:
|
|
/// - the instruction to allocate the local variable is a custom opaque
|
|
/// function
|
|
/// - it's address is taken with AddressOf, whose type on LLVM is a
|
|
/// pointer-sized integer type
|
|
///
|
|
/// In non-legacy mode:
|
|
/// - the instruction to allocate the local variable is a regular alloca
|
|
/// - the alloca is ptr-to-int casted to a pointer-sized integer.custom opaque
|
|
///
|
|
/// TODO: this method can become const when we drop legacy mode.
|
|
std::pair<LocalVarType *, llvm::Instruction *>
|
|
createLocalVariableAndTakeIntAddress(const model::Type &VariableType);
|
|
|
|
/// Creates an llvm::Instruction that models the allocation of a local
|
|
/// variable representing the stack frame, and takes its address.
|
|
/// The returned llvm::Instruction has an integer type on LLVM, and its size
|
|
/// is equal to the size of a pointer in the associated Model.Architecture.
|
|
/// The instruction that represents the allocation of the local variable is
|
|
/// inserted at the beginning of function F, after all the allocas.
|
|
///
|
|
/// In legacy mode:
|
|
/// - the instruction to allocate the local variable is a custom opaque
|
|
/// function
|
|
/// - it's address is taken with AddressOf, whose type on LLVM is a
|
|
/// pointer-sized integer type
|
|
///
|
|
/// In non-legacy mode:
|
|
/// - the instruction to allocate the local variable is a regular alloca
|
|
/// - the alloca is ptr-to-int casted to a pointer-sized integer.custom opaque
|
|
///
|
|
/// TODO: this method can become const when we drop legacy mode.
|
|
llvm::Instruction *createStackFrameVariable();
|
|
|
|
/// Creates an llvm::Instruction that models the allocation of a local
|
|
/// variable to be passed as stack argument to a call instruction, and take
|
|
/// its address.
|
|
/// The returned llvm::Instruction has an integer type on LLVM, and its size
|
|
/// is equal to the size of a pointer in the associated Model.Architecture.
|
|
/// The instruction that represents the allocation of the local variable is
|
|
/// inserted at the beginning of function F, after all the allocas.
|
|
///
|
|
/// In legacy mode:
|
|
/// - the instruction to allocate the local variable is a custom opaque
|
|
/// function
|
|
/// - it's address is taken with AddressOf, whose type on LLVM is a
|
|
/// pointer-sized integer type
|
|
///
|
|
/// In non-legacy mode:
|
|
/// - the instruction to allocate the local variable is a regular alloca
|
|
/// - the alloca is ptr-to-int casted to a pointer-sized integer.custom opaque
|
|
///
|
|
/// TODO: this method can be dropped when we drop legacy mode, because the
|
|
/// callers can just switch to call createLocalVariableAndTakeAddress
|
|
llvm::Instruction *
|
|
createCallStackArgumentVariable(const model::Type &VariableType);
|
|
|
|
public:
|
|
/// Takes an instruction representing a variable location and a Use, and
|
|
/// replaces the Use with a copy instruction from the instruction representing
|
|
/// the variable location
|
|
///
|
|
/// In legacy mode an instruction representing a variable location should be
|
|
/// a call to an opaque function tagged with FunctionTags::IsRef. A copy
|
|
/// instruction is a call to Copy.
|
|
/// In non-legacy mode an instruction representing a variable location should
|
|
/// be a ptr-typed instruction, and copy is a LoadInst.
|
|
///
|
|
/// TODO: This method can be made const whenever we drop legacy mode.
|
|
CopyType *createCopyOnUse(ReferenceType *LocationToCopy, llvm::Use &U);
|
|
|
|
/// Takes an assignment instruction and a Use and replaces the Use with a
|
|
/// newly created copy of the location assigned by the assignment instruction.
|
|
///
|
|
/// In legacy mode an assignment instruction is a call to Assign and a copy
|
|
/// instruction is a call to Copy.
|
|
/// In non-legacy mode an assignment instruction is just a StoreInst, and copy
|
|
/// a LoadInst.
|
|
///
|
|
/// TODO: This method can be made const whenever we drop legacy mode.
|
|
CopyType *createCopyFromAssignedOnUse(AssignType *Assign, llvm::Use &U);
|
|
|
|
/// Creates an assignment instruction, at the location specified by
|
|
/// InsertBefore, assigning ValueToAssign to the location represented by
|
|
/// LocationToAssign.
|
|
///
|
|
/// In legacy mode an assignment instruction is a call to Assign.
|
|
/// In non-legacy mode an assignment instruction is just a StoreInst.
|
|
///
|
|
/// TODO: This method can be made const whenever we drop legacy mode.
|
|
AssignType *createAssignmentBefore(llvm::Value *LocationToAssign,
|
|
llvm::Value *ValueToAssign,
|
|
llvm::Instruction *InsertBefore);
|
|
|
|
/// Creates an alloca in \a F with type \a T.
|
|
/// Allocas created with this method are intended to be inserted temporarily,
|
|
/// and subsequently optimized away from LLVM optimizations.
|
|
/// There's no need to tag them with model::Types in any way.
|
|
std::pair<llvm::AllocaInst *, llvm::Value *>
|
|
createAllocaWithPtrToInt(llvm::Function *F, llvm::Type *T) const;
|
|
|
|
private:
|
|
/// Takes an assignment instruction and returns its operand that represents
|
|
/// the assigned location.
|
|
/// In legacy mode an assignment instruction is a call to Assign.
|
|
/// In non-legacy mode an assignment instruction is just a StoreInst.
|
|
ReferenceType *getAssignedLocation(AssignType *Assign) const;
|
|
|
|
/// Legacy methods for lazily initializing the StackFrameAllocator and
|
|
/// CallStackArgumentsAllocator, in Legacy mode.
|
|
///
|
|
/// TODO: drop these when we drop legacy mode
|
|
///@{
|
|
|
|
llvm::Function *getStackFrameAllocator();
|
|
|
|
llvm::Function *getCallStackArgumentsAllocator();
|
|
|
|
///@}
|
|
};
|