Files
2021-06-09 20:13:27 +02:00

946 lines
24 KiB
C++

#pragma once
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include "llvm/ADT/SmallString.h"
#include "revng/ADT/KeyedObjectContainer.h"
#include "revng/ADT/MutableSet.h"
#include "revng/ADT/SortedVector.h"
#include "revng/ADT/UpcastablePointer.h"
#include "revng/ADT/UpcastablePointer/YAMLTraits.h"
#include "revng/Model/TupleTree.h"
#include "revng/Support/MetaAddress.h"
#include "revng/Support/MetaAddress/YAMLTraits.h"
#include "revng/Support/YAMLTraits.h"
// Forward declarations
namespace model {
class Function;
class Binary;
class FunctionEdge;
class CallEdge;
class FunctionABIRegister;
class BasicBlock;
} // namespace model
// TODO: Prevent changing the keys. Currently we need them to be public and
// non-const for serialization purposes.
template<>
struct KeyedObjectTraits<MetaAddress>
: public IdentityKeyedObjectTraits<MetaAddress> {};
namespace model::Register {
enum Values {
Invalid,
// x86 registers
eax_x86,
ebx_x86,
ecx_x86,
edx_x86,
esi_x86,
edi_x86,
ebp_x86,
esp_x86,
// x86-64 registers
rax_x86_64,
rbx_x86_64,
rcx_x86_64,
rdx_x86_64,
rbp_x86_64,
rsp_x86_64,
rsi_x86_64,
rdi_x86_64,
r8_x86_64,
r9_x86_64,
r10_x86_64,
r11_x86_64,
r12_x86_64,
r13_x86_64,
r14_x86_64,
r15_x86_64,
xmm0_x86_64,
xmm1_x86_64,
xmm2_x86_64,
xmm3_x86_64,
xmm4_x86_64,
xmm5_x86_64,
xmm6_x86_64,
xmm7_x86_64,
// ARM registers
r0_arm,
r1_arm,
r2_arm,
r3_arm,
r4_arm,
r5_arm,
r6_arm,
r7_arm,
r8_arm,
r9_arm,
r10_arm,
r11_arm,
r12_arm,
r13_arm,
r14_arm,
// AArch64 registers
x0_aarch64,
x1_aarch64,
x2_aarch64,
x3_aarch64,
x4_aarch64,
x5_aarch64,
x6_aarch64,
x7_aarch64,
x8_aarch64,
x9_aarch64,
x10_aarch64,
x11_aarch64,
x12_aarch64,
x13_aarch64,
x14_aarch64,
x15_aarch64,
x16_aarch64,
x17_aarch64,
x18_aarch64,
x19_aarch64,
x20_aarch64,
x21_aarch64,
x22_aarch64,
x23_aarch64,
x24_aarch64,
x25_aarch64,
x26_aarch64,
x27_aarch64,
x28_aarch64,
x29_aarch64,
lr_aarch64,
sp_aarch64,
// MIPS registers
v0_mips,
v1_mips,
a0_mips,
a1_mips,
a2_mips,
a3_mips,
s0_mips,
s1_mips,
s2_mips,
s3_mips,
s4_mips,
s5_mips,
s6_mips,
s7_mips,
gp_mips,
sp_mips,
fp_mips,
ra_mips,
// SystemZ registers
r0_systemz,
r1_systemz,
r2_systemz,
r3_systemz,
r4_systemz,
r5_systemz,
r6_systemz,
r7_systemz,
r8_systemz,
r9_systemz,
r10_systemz,
r11_systemz,
r12_systemz,
r13_systemz,
r14_systemz,
r15_systemz,
f0_systemz,
f1_systemz,
f2_systemz,
f3_systemz,
f4_systemz,
f5_systemz,
f6_systemz,
f7_systemz,
f8_systemz,
f9_systemz,
f10_systemz,
f11_systemz,
f12_systemz,
f13_systemz,
f14_systemz,
f15_systemz
};
} // end namespace model::Register
namespace llvm::yaml {
template<>
struct ScalarEnumerationTraits<model::Register::Values> {
template<typename T>
static void enumeration(T &io, model::Register::Values &V) {
using namespace model::Register;
io.enumCase(V, "Invalid", Invalid);
io.enumCase(V, "eax_x86", eax_x86);
io.enumCase(V, "ebx_x86", ebx_x86);
io.enumCase(V, "ecx_x86", ecx_x86);
io.enumCase(V, "edx_x86", edx_x86);
io.enumCase(V, "esi_x86", esi_x86);
io.enumCase(V, "edi_x86", edi_x86);
io.enumCase(V, "ebp_x86", ebp_x86);
io.enumCase(V, "esp_x86", esp_x86);
io.enumCase(V, "rax_x86_64", rax_x86_64);
io.enumCase(V, "rbx_x86_64", rbx_x86_64);
io.enumCase(V, "rcx_x86_64", rcx_x86_64);
io.enumCase(V, "rdx_x86_64", rdx_x86_64);
io.enumCase(V, "rbp_x86_64", rbp_x86_64);
io.enumCase(V, "rsp_x86_64", rsp_x86_64);
io.enumCase(V, "rsi_x86_64", rsi_x86_64);
io.enumCase(V, "rdi_x86_64", rdi_x86_64);
io.enumCase(V, "r8_x86_64", r8_x86_64);
io.enumCase(V, "r9_x86_64", r9_x86_64);
io.enumCase(V, "r10_x86_64", r10_x86_64);
io.enumCase(V, "r11_x86_64", r11_x86_64);
io.enumCase(V, "r12_x86_64", r12_x86_64);
io.enumCase(V, "r13_x86_64", r13_x86_64);
io.enumCase(V, "r14_x86_64", r14_x86_64);
io.enumCase(V, "r15_x86_64", r15_x86_64);
io.enumCase(V, "xmm0_x86_64", xmm0_x86_64);
io.enumCase(V, "xmm1_x86_64", xmm1_x86_64);
io.enumCase(V, "xmm2_x86_64", xmm2_x86_64);
io.enumCase(V, "xmm3_x86_64", xmm3_x86_64);
io.enumCase(V, "xmm4_x86_64", xmm4_x86_64);
io.enumCase(V, "xmm5_x86_64", xmm5_x86_64);
io.enumCase(V, "xmm6_x86_64", xmm6_x86_64);
io.enumCase(V, "xmm7_x86_64", xmm7_x86_64);
io.enumCase(V, "r0_arm", r0_arm);
io.enumCase(V, "r1_arm", r1_arm);
io.enumCase(V, "r2_arm", r2_arm);
io.enumCase(V, "r3_arm", r3_arm);
io.enumCase(V, "r4_arm", r4_arm);
io.enumCase(V, "r5_arm", r5_arm);
io.enumCase(V, "r6_arm", r6_arm);
io.enumCase(V, "r7_arm", r7_arm);
io.enumCase(V, "r8_arm", r8_arm);
io.enumCase(V, "r9_arm", r9_arm);
io.enumCase(V, "r10_arm", r10_arm);
io.enumCase(V, "r11_arm", r11_arm);
io.enumCase(V, "r12_arm", r12_arm);
io.enumCase(V, "r13_arm", r13_arm);
io.enumCase(V, "r14_arm", r14_arm);
io.enumCase(V, "x0_aarch64", x0_aarch64);
io.enumCase(V, "x1_aarch64", x1_aarch64);
io.enumCase(V, "x2_aarch64", x2_aarch64);
io.enumCase(V, "x3_aarch64", x3_aarch64);
io.enumCase(V, "x4_aarch64", x4_aarch64);
io.enumCase(V, "x5_aarch64", x5_aarch64);
io.enumCase(V, "x6_aarch64", x6_aarch64);
io.enumCase(V, "x7_aarch64", x7_aarch64);
io.enumCase(V, "x8_aarch64", x8_aarch64);
io.enumCase(V, "x9_aarch64", x9_aarch64);
io.enumCase(V, "x10_aarch64", x10_aarch64);
io.enumCase(V, "x11_aarch64", x11_aarch64);
io.enumCase(V, "x12_aarch64", x12_aarch64);
io.enumCase(V, "x13_aarch64", x13_aarch64);
io.enumCase(V, "x14_aarch64", x14_aarch64);
io.enumCase(V, "x15_aarch64", x15_aarch64);
io.enumCase(V, "x16_aarch64", x16_aarch64);
io.enumCase(V, "x17_aarch64", x17_aarch64);
io.enumCase(V, "x18_aarch64", x18_aarch64);
io.enumCase(V, "x19_aarch64", x19_aarch64);
io.enumCase(V, "x20_aarch64", x20_aarch64);
io.enumCase(V, "x21_aarch64", x21_aarch64);
io.enumCase(V, "x22_aarch64", x22_aarch64);
io.enumCase(V, "x23_aarch64", x23_aarch64);
io.enumCase(V, "x24_aarch64", x24_aarch64);
io.enumCase(V, "x25_aarch64", x25_aarch64);
io.enumCase(V, "x26_aarch64", x26_aarch64);
io.enumCase(V, "x27_aarch64", x27_aarch64);
io.enumCase(V, "x28_aarch64", x28_aarch64);
io.enumCase(V, "x29_aarch64", x29_aarch64);
io.enumCase(V, "lr_aarch64", lr_aarch64);
io.enumCase(V, "sp_aarch64", sp_aarch64);
io.enumCase(V, "v0_mips", v0_mips);
io.enumCase(V, "v1_mips", v1_mips);
io.enumCase(V, "a0_mips", a0_mips);
io.enumCase(V, "a1_mips", a1_mips);
io.enumCase(V, "a2_mips", a2_mips);
io.enumCase(V, "a3_mips", a3_mips);
io.enumCase(V, "s0_mips", s0_mips);
io.enumCase(V, "s1_mips", s1_mips);
io.enumCase(V, "s2_mips", s2_mips);
io.enumCase(V, "s3_mips", s3_mips);
io.enumCase(V, "s4_mips", s4_mips);
io.enumCase(V, "s5_mips", s5_mips);
io.enumCase(V, "s6_mips", s6_mips);
io.enumCase(V, "s7_mips", s7_mips);
io.enumCase(V, "gp_mips", gp_mips);
io.enumCase(V, "sp_mips", sp_mips);
io.enumCase(V, "fp_mips", fp_mips);
io.enumCase(V, "ra_mips", ra_mips);
io.enumCase(V, "r0_systemz", r0_systemz);
io.enumCase(V, "r1_systemz", r1_systemz);
io.enumCase(V, "r2_systemz", r2_systemz);
io.enumCase(V, "r3_systemz", r3_systemz);
io.enumCase(V, "r4_systemz", r4_systemz);
io.enumCase(V, "r5_systemz", r5_systemz);
io.enumCase(V, "r6_systemz", r6_systemz);
io.enumCase(V, "r7_systemz", r7_systemz);
io.enumCase(V, "r8_systemz", r8_systemz);
io.enumCase(V, "r9_systemz", r9_systemz);
io.enumCase(V, "r10_systemz", r10_systemz);
io.enumCase(V, "r11_systemz", r11_systemz);
io.enumCase(V, "r12_systemz", r12_systemz);
io.enumCase(V, "r13_systemz", r13_systemz);
io.enumCase(V, "r14_systemz", r14_systemz);
io.enumCase(V, "r15_systemz", r15_systemz);
io.enumCase(V, "f0_systemz", f0_systemz);
io.enumCase(V, "f1_systemz", f1_systemz);
io.enumCase(V, "f2_systemz", f2_systemz);
io.enumCase(V, "f3_systemz", f3_systemz);
io.enumCase(V, "f4_systemz", f4_systemz);
io.enumCase(V, "f5_systemz", f5_systemz);
io.enumCase(V, "f6_systemz", f6_systemz);
io.enumCase(V, "f7_systemz", f7_systemz);
io.enumCase(V, "f8_systemz", f8_systemz);
io.enumCase(V, "f9_systemz", f9_systemz);
io.enumCase(V, "f10_systemz", f10_systemz);
io.enumCase(V, "f11_systemz", f11_systemz);
io.enumCase(V, "f12_systemz", f12_systemz);
io.enumCase(V, "f13_systemz", f13_systemz);
io.enumCase(V, "f14_systemz", f14_systemz);
io.enumCase(V, "f15_systemz", f15_systemz);
}
};
} // namespace llvm::yaml
namespace model::Register {
inline llvm::StringRef getName(Values V) {
return getNameFromYAMLEnumScalar(V);
}
inline llvm::StringRef getRegisterName(Values V) {
llvm::StringRef FullName = getName(V);
switch (V) {
case Invalid:
revng_abort();
case eax_x86:
case ebx_x86:
case ecx_x86:
case edx_x86:
case esi_x86:
case edi_x86:
case ebp_x86:
case esp_x86:
return FullName.substr(0, FullName.size() - sizeof("_x86") + 1);
case rax_x86_64:
case rbx_x86_64:
case rcx_x86_64:
case rdx_x86_64:
case rbp_x86_64:
case rsp_x86_64:
case rsi_x86_64:
case rdi_x86_64:
case r8_x86_64:
case r9_x86_64:
case r10_x86_64:
case r11_x86_64:
case r12_x86_64:
case r13_x86_64:
case r14_x86_64:
case r15_x86_64:
case xmm0_x86_64:
case xmm1_x86_64:
case xmm2_x86_64:
case xmm3_x86_64:
case xmm4_x86_64:
case xmm5_x86_64:
case xmm6_x86_64:
case xmm7_x86_64:
return FullName.substr(0, FullName.size() - sizeof("_x86_64") + 1);
case r0_arm:
case r1_arm:
case r2_arm:
case r3_arm:
case r4_arm:
case r5_arm:
case r6_arm:
case r7_arm:
case r8_arm:
case r9_arm:
case r10_arm:
case r11_arm:
case r12_arm:
case r13_arm:
case r14_arm:
return FullName.substr(0, FullName.size() - sizeof("_arm") + 1);
case x0_aarch64:
case x1_aarch64:
case x2_aarch64:
case x3_aarch64:
case x4_aarch64:
case x5_aarch64:
case x6_aarch64:
case x7_aarch64:
case x8_aarch64:
case x9_aarch64:
case x10_aarch64:
case x11_aarch64:
case x12_aarch64:
case x13_aarch64:
case x14_aarch64:
case x15_aarch64:
case x16_aarch64:
case x17_aarch64:
case x18_aarch64:
case x19_aarch64:
case x20_aarch64:
case x21_aarch64:
case x22_aarch64:
case x23_aarch64:
case x24_aarch64:
case x25_aarch64:
case x26_aarch64:
case x27_aarch64:
case x28_aarch64:
case x29_aarch64:
case lr_aarch64:
case sp_aarch64:
return FullName.substr(0, FullName.size() - sizeof("_aarch64") + 1);
case v0_mips:
case v1_mips:
case a0_mips:
case a1_mips:
case a2_mips:
case a3_mips:
case s0_mips:
case s1_mips:
case s2_mips:
case s3_mips:
case s4_mips:
case s5_mips:
case s6_mips:
case s7_mips:
case gp_mips:
case sp_mips:
case fp_mips:
case ra_mips:
return FullName.substr(0, FullName.size() - sizeof("_mips") + 1);
case r0_systemz:
case r1_systemz:
case r2_systemz:
case r3_systemz:
case r4_systemz:
case r5_systemz:
case r6_systemz:
case r7_systemz:
case r8_systemz:
case r9_systemz:
case r10_systemz:
case r11_systemz:
case r12_systemz:
case r13_systemz:
case r14_systemz:
case r15_systemz:
case f0_systemz:
case f1_systemz:
case f2_systemz:
case f3_systemz:
case f4_systemz:
case f5_systemz:
case f6_systemz:
case f7_systemz:
case f8_systemz:
case f9_systemz:
case f10_systemz:
case f11_systemz:
case f12_systemz:
case f13_systemz:
case f14_systemz:
case f15_systemz:
return FullName.substr(0, FullName.size() - sizeof("_systemz") + 1);
}
}
inline Values fromName(llvm::StringRef Name) {
return getValueFromYAMLScalar<Values>(Name);
}
inline Values
fromRegisterName(llvm::StringRef Name, llvm::Triple::ArchType Arch) {
std::string FullName = Name.str();
switch (Arch) {
case llvm::Triple::x86:
FullName += "_x86";
break;
case llvm::Triple::x86_64:
FullName += "_x86_64";
break;
case llvm::Triple::arm:
FullName += "_arm";
break;
case llvm::Triple::aarch64:
FullName += "_aarch64";
break;
case llvm::Triple::mips:
case llvm::Triple::mipsel:
FullName += "_mips";
break;
case llvm::Triple::systemz:
FullName += "_systemz";
break;
default:
return Invalid;
}
return getValueFromYAMLScalar<Values>(FullName);
}
} // namespace model::Register
template<>
inline model::Register::Values
getInvalidValueFromYAMLScalar<model::Register::Values>() {
return model::Register::Invalid;
}
namespace model::RegisterState {
enum Values {
Invalid,
No,
NoOrDead,
Dead,
Yes,
YesOrDead,
Maybe,
Contradiction
};
} // namespace model::RegisterState
namespace llvm::yaml {
template<>
struct ScalarEnumerationTraits<model::RegisterState::Values> {
template<typename T>
static void enumeration(T &io, model::RegisterState::Values &V) {
using namespace model::RegisterState;
io.enumCase(V, "Invalid", Invalid);
io.enumCase(V, "No", No, QuotingType::Double);
io.enumCase(V, "NoOrDead", NoOrDead);
io.enumCase(V, "Dead", Dead);
io.enumCase(V, "Yes", Yes, QuotingType::Double);
io.enumCase(V, "YesOrDead", YesOrDead);
io.enumCase(V, "Maybe", Maybe);
io.enumCase(V, "Contradiction", Contradiction);
}
};
} // namespace llvm::yaml
namespace model::RegisterState {
inline llvm::StringRef getName(Values V) {
return getNameFromYAMLEnumScalar(V);
}
inline Values fromName(llvm::StringRef Name) {
return getValueFromYAMLScalar<Values>(Name);
}
inline bool shouldEmit(model::RegisterState::Values V) {
return (V == model::RegisterState::Yes or V == model::RegisterState::YesOrDead
or V == model::RegisterState::Dead);
}
} // namespace model::RegisterState
class model::FunctionABIRegister {
public:
Register::Values Register;
RegisterState::Values Argument = RegisterState::Invalid;
RegisterState::Values ReturnValue = RegisterState::Invalid;
public:
FunctionABIRegister(const Register::Values &Register) : Register(Register) {}
bool operator==(const FunctionABIRegister &Other) const = default;
public:
bool verify() const debug_function {
return (Register != Register::Invalid and Argument != RegisterState::Invalid
and ReturnValue != RegisterState::Invalid);
}
};
INTROSPECTION_NS(model, FunctionABIRegister, Register, Argument, ReturnValue);
template<>
struct llvm::yaml::MappingTraits<model::FunctionABIRegister>
: public TupleLikeMappingTraits<model::FunctionABIRegister> {};
template<>
struct KeyedObjectTraits<model::FunctionABIRegister> {
static model::Register::Values key(const model::FunctionABIRegister &Obj) {
return Obj.Register;
}
static model::FunctionABIRegister
fromKey(const model::Register::Values &Register) {
return model::FunctionABIRegister(Register);
}
};
//
// FunctionEdgeType
//
// TODO: we need to handle noreturn function calls
/// Type of edge on the CFG
namespace model::FunctionEdgeType {
enum Values {
/// Invalid value
Invalid,
/// Branch due to function-local CFG (a regular branch)
DirectBranch,
/// A call to a fake function
FakeFunctionCall,
/// A return from a fake function
FakeFunctionReturn,
/// A function call for which the cache was able to produce a summary
FunctionCall,
/// A function call for which the target is unknown
IndirectCall,
/// A proper function return
Return,
/// A branch returning to the return address, but leaving the stack
/// in an unexpected situation
BrokenReturn,
/// A branch representing an indirect tail call
IndirectTailCall,
/// A branch representing a longjmp or similar constructs
LongJmp,
/// A killer basic block (killer syscall or endless loop)
Killer,
/// The basic block ends with an unreachable instruction
Unreachable
};
inline bool hasDestination(Values V) {
switch (V) {
case Invalid:
revng_abort();
break;
case DirectBranch:
case FakeFunctionCall:
case FakeFunctionReturn:
case FunctionCall:
return true;
case IndirectCall:
case Return:
case BrokenReturn:
case IndirectTailCall:
case LongJmp:
case Killer:
case Unreachable:
return false;
}
}
inline bool isCall(Values V) {
switch (V) {
case FunctionCall:
case IndirectCall:
case IndirectTailCall:
return true;
case Invalid:
case DirectBranch:
case FakeFunctionCall:
case FakeFunctionReturn:
case Return:
case BrokenReturn:
case LongJmp:
case Killer:
case Unreachable:
return false;
}
}
} // namespace model::FunctionEdgeType
namespace llvm::yaml {
template<>
struct ScalarEnumerationTraits<model::FunctionEdgeType::Values> {
template<typename T>
static void enumeration(T &io, model::FunctionEdgeType::Values &V) {
using namespace model::FunctionEdgeType;
io.enumCase(V, "Invalid", Invalid);
io.enumCase(V, "DirectBranch", DirectBranch);
io.enumCase(V, "FakeFunctionCall", FakeFunctionCall);
io.enumCase(V, "FakeFunctionReturn", FakeFunctionReturn);
io.enumCase(V, "FunctionCall", FunctionCall);
io.enumCase(V, "IndirectCall", IndirectCall);
io.enumCase(V, "Return", Return);
io.enumCase(V, "BrokenReturn", BrokenReturn);
io.enumCase(V, "IndirectTailCall", IndirectTailCall);
io.enumCase(V, "LongJmp", LongJmp);
io.enumCase(V, "Killer", Killer);
io.enumCase(V, "Unreachable", Unreachable);
}
};
} // namespace llvm::yaml
namespace model::FunctionEdgeType {
inline llvm::StringRef getName(Values V) {
return getNameFromYAMLEnumScalar(V);
}
inline Values fromName(llvm::StringRef Name) {
return getValueFromYAMLScalar<Values>(Name);
}
} // namespace model::FunctionEdgeType
//
// FunctionEdge
//
/// An edge on the CFG
class model::FunctionEdge {
public:
using Key = std::pair<MetaAddress, model::FunctionEdgeType::Values>;
public:
/// Edge target. If invalid, it's an indirect edge
MetaAddress Destination;
FunctionEdgeType::Values Type;
public:
FunctionEdge() :
Destination(MetaAddress::invalid()), Type(FunctionEdgeType::Invalid) {}
FunctionEdge(MetaAddress Destination, FunctionEdgeType::Values Type) :
Destination(Destination), Type(Type) {}
public:
bool operator==(const FunctionEdge &Other) const = default;
bool operator<(const FunctionEdge &Other) const {
auto ThisTie = std::tie(Destination, Type);
auto OtherTie = std::tie(Other.Destination, Other.Type);
return ThisTie < OtherTie;
}
public:
static constexpr const char *Tag = "!FunctionEdge";
static bool classof(const FunctionEdge *A) {
return not FunctionEdgeType::isCall(A->Type);
}
bool verify() const debug_function;
};
INTROSPECTION_NS(model, FunctionEdge, Destination, Type);
class model::CallEdge : public model::FunctionEdge {
public:
using Key = std::pair<MetaAddress, model::FunctionEdgeType::Values>;
public:
SortedVector<model::FunctionABIRegister> Registers;
public:
CallEdge() :
FunctionEdge(MetaAddress::invalid(), FunctionEdgeType::FunctionCall) {}
CallEdge(MetaAddress Destination, FunctionEdgeType::Values Type) :
FunctionEdge(Destination, Type) {
revng_assert(FunctionEdgeType::isCall(Type));
}
public:
static constexpr const char *Tag = "!CallEdge";
static bool classof(const FunctionEdge *A) {
return FunctionEdgeType::isCall(A->Type);
}
public:
bool verify() const debug_function;
};
INTROSPECTION_NS(model, CallEdge, Destination, Type, Registers);
template<>
struct concrete_types_traits<model::FunctionEdge> {
using type = std::tuple<model::CallEdge, model::FunctionEdge>;
};
template<>
class llvm::yaml::MappingTraits<UpcastablePointer<model::FunctionEdge>>
: public PolymorphicMappingTraits<UpcastablePointer<model::FunctionEdge>> {};
template<>
struct llvm::yaml::MappingTraits<model::FunctionEdge>
: public TupleLikeMappingTraits<model::FunctionEdge> {};
template<>
struct llvm::yaml::MappingTraits<model::CallEdge>
: public TupleLikeMappingTraits<model::CallEdge> {};
template<>
struct llvm::yaml::ScalarTraits<model::FunctionEdge::Key>
: public CompositeScalar<model::FunctionEdge::Key, '-'> {};
template<>
struct KeyedObjectTraits<model::FunctionEdge> {
using Key = model::FunctionEdge::Key;
static Key key(const model::FunctionEdge &Obj) {
return { Obj.Destination, Obj.Type };
}
static model::FunctionEdge fromKey(const Key &Obj) {
return model::FunctionEdge{ Obj.first, Obj.second };
}
};
template<>
struct KeyedObjectTraits<UpcastablePointer<model::FunctionEdge>> {
using Key = model::FunctionEdge::Key;
static Key key(const UpcastablePointer<model::FunctionEdge> &Obj) {
return { Obj->Destination, Obj->Type };
}
static UpcastablePointer<model::FunctionEdge> fromKey(const Key &Obj) {
using ResultType = UpcastablePointer<model::FunctionEdge>;
if (model::FunctionEdgeType::isCall(Obj.second)) {
return ResultType(new model::CallEdge(Obj.first, Obj.second));
} else {
return ResultType(new model::FunctionEdge(Obj.first, Obj.second));
}
}
};
//
// FunctionType
//
namespace model::FunctionType {
enum Values {
Invalid, ///< An invalid entry
Regular, ///< A normal function
NoReturn, ///< A noreturn function
Fake ///< A fake function
};
}
namespace llvm::yaml {
template<>
struct ScalarEnumerationTraits<model::FunctionType::Values> {
static void enumeration(IO &io, model::FunctionType::Values &V) {
using namespace model::FunctionType;
io.enumCase(V, "Invalid", Invalid);
io.enumCase(V, "Regular", Regular);
io.enumCase(V, "NoReturn", NoReturn);
io.enumCase(V, "Fake", Fake);
}
};
} // namespace llvm::yaml
class model::BasicBlock {
public:
MetaAddress Start;
MetaAddress End;
std::string Name;
SortedVector<UpcastablePointer<model::FunctionEdge>> Successors;
public:
BasicBlock(const MetaAddress &Start) : Start(Start) {}
bool operator==(const model::BasicBlock &Other) const = default;
};
INTROSPECTION_NS(model, BasicBlock, Start, End, Name, Successors);
template<>
struct llvm::yaml::MappingTraits<model::BasicBlock>
: public TupleLikeMappingTraits<model::BasicBlock> {};
template<>
struct KeyedObjectTraits<model::BasicBlock> {
static MetaAddress key(const model::BasicBlock &Obj) { return Obj.Start; }
static model::BasicBlock fromKey(const MetaAddress &Obj) {
return model::BasicBlock(Obj);
}
};
//
// Function
//
class model::Function {
public:
MetaAddress Entry;
std::string Name;
FunctionType::Values Type;
SortedVector<model::BasicBlock> CFG;
SortedVector<model::FunctionABIRegister> Registers;
public:
Function(const MetaAddress &Entry) : Entry(Entry) {}
bool operator==(const model::Function &Other) const = default;
public:
bool verify() const debug_function;
void dumpCFG() const debug_function;
};
INTROSPECTION_NS(model, Function, Entry, Name, Type, CFG, Registers)
template<>
struct llvm::yaml::MappingTraits<model::Function>
: public TupleLikeMappingTraits<model::Function> {};
template<>
struct KeyedObjectTraits<model::Function> {
static MetaAddress key(const model::Function &F) { return F.Entry; }
static model::Function fromKey(const MetaAddress &Key) {
return model::Function(Key);
};
};
static_assert(IsKeyedObjectContainer<MutableSet<model::Function>>);
//
// Binary
//
class model::Binary {
public:
MutableSet<model::Function> Functions;
public:
bool verify() const debug_function;
};
INTROSPECTION_NS(model, Binary, Functions)
template<>
struct llvm::yaml::MappingTraits<model::Binary>
: public TupleLikeMappingTraits<model::Binary> {};
static_assert(validateTupleTree<model::Binary>(IsYamlizable),
"All elements of the model must be YAMLizable");
constexpr auto OnlyKOC = [](auto *K) {
using type = std::remove_pointer_t<decltype(K)>;
if constexpr (IsContainer<type>) {
if constexpr (IsKeyedObjectContainer<type>) {
using value_type = typename type::value_type;
using KOT = KeyedObjectTraits<value_type>;
using KeyType = decltype(KOT::key(std::declval<value_type>()));
return Yamlizable<KeyType>;
} else {
return false;
}
} else {
return true;
}
};
static_assert(validateTupleTree<model::Binary>(OnlyKOC),
"Only SortedVectors and MutableSets with YAMLizable keys are "
"allowed");