Files
revng-revng/lib/Model/Type.cpp
T
Pietro Fezzardi 29c50fa909 Model: reserve names defined in stdint.h
This commit adds a bunch of type names and macro names to the set of
reserved keywords that cannot be used for names in the revng Model.
These are the names defined in the stdint.h standard C header, that is
included automatically by the decompiler in decompiled code.

We need to reserve these names otherwise it would be possible to add
things to the revng Model with names that clash with the names defined
in stdint.h, which would in turn break recompilation of decompiled code
because of conflicting definitions.
2022-02-08 15:54:02 +01:00

1287 lines
32 KiB
C++

//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include <bit>
#include <cstddef>
#include <random>
#include <string>
#include <type_traits>
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/Support/MathExtras.h"
#include "revng/Model/Binary.h"
#include "revng/Model/Register.h"
#include "revng/Model/VerifyHelper.h"
using llvm::cast;
using llvm::dyn_cast;
using llvm::Twine;
namespace model {
const Identifier Identifier::Empty = Identifier("");
const std::set<llvm::StringRef> ReservedKeywords = {
// reserved keywords for primitive types
"void",
"pointer_or_number8_t",
"pointer_or_number16_t",
"pointer_or_number32_t",
"pointer_or_number64_t",
"pointer_or_number128_t",
"number8_t",
"number16_t",
"number32_t",
"number64_t",
"number128_t",
"generic8_t",
"generic16_t",
"generic32_t",
"generic64_t",
"generic128_t",
"int8_t",
"int16_t",
"int32_t",
"int64_t",
"int128_t",
"int_fast8_t",
"int_fast16_t",
"int_fast32_t",
"int_fast64_t",
"int_fast128_t",
"int_least8_t",
"int_least16_t",
"int_least32_t",
"int_least64_t",
"int_least128_t",
"intmax_t",
"intptr_t",
"uint8_t",
"uint16_t",
"uint32_t",
"uint64_t",
"uint128_t",
"uint_fast8_t",
"uint_fast16_t",
"uint_fast32_t",
"uint_fast64_t",
"uint_fast128_t",
"uint_least8_t",
"uint_least16_t",
"uint_least32_t",
"uint_least64_t",
"uint_least128_t",
"uintmax_t",
"uintptr_t",
"float16_t",
"float32_t",
"float64_t",
"float128_t",
// Integer macros from stdint.h, reserved to prevent clashes.
"INT8_WIDTH",
"INT16_WIDTH",
"INT32_WIDTH",
"INT64_WIDTH",
"INT_FAST8_WIDTH",
"INT_FAST16_WIDTH",
"INT_FAST32_WIDTH",
"INT_FAST64_WIDTH",
"INT_LEAST8_WIDTH",
"INT_LEAST16_WIDTH",
"INT_LEAST32_WIDTH",
"INT_LEAST64_WIDTH",
"INTPTR_WIDTH",
"INTMAX_WIDTH",
"INT8_MIN",
"INT16_MIN",
"INT32_MIN",
"INT64_MIN",
"INT_FAST8_MIN",
"INT_FAST16_MIN",
"INT_FAST32_MIN",
"INT_FAST64_MIN",
"INT_LEAST8_MIN",
"INT_LEAST16_MIN",
"INT_LEAST32_MIN",
"INT_LEAST64_MIN",
"INTPTR_MIN",
"INTMAX_MIN",
"INT8_MAX",
"INT16_MAX",
"INT32_MAX",
"INT64_MAX",
"INT_FAST8_MAX",
"INT_FAST16_MAX",
"INT_FAST32_MAX",
"INT_FAST64_MAX",
"INT_LEAST8_MAX",
"INT_LEAST16_MAX",
"INT_LEAST32_MAX",
"INT_LEAST64_MAX",
"INTPTR_MAX",
"INTMAX_MAX",
"UINT8_WIDTH",
"UINT16_WIDTH",
"UINT32_WIDTH",
"UINT64_WIDTH",
"UINT_FAST8_WIDTH",
"UINT_FAST16_WIDTH",
"UINT_FAST32_WIDTH",
"UINT_FAST64_WIDTH",
"UINT_LEAST8_WIDTH",
"UINT_LEAST16_WIDTH",
"UINT_LEAST32_WIDTH",
"UINT_LEAST64_WIDTH",
"UINTPTR_WIDTH",
"UINTMAX_WIDTH",
"UINT8_MAX",
"UINT16_MAX",
"UINT32_MAX",
"UINT64_MAX",
"UINT_FAST8_MAX",
"UINT_FAST16_MAX",
"UINT_FAST32_MAX",
"UINT_FAST64_MAX",
"UINT_LEAST8_MAX",
"UINT_LEAST16_MAX",
"UINT_LEAST32_MAX",
"UINT_LEAST64_MAX",
"UINTPTR_MAX",
"UINTMAX_MAX",
"INT8_C",
"INT16_C",
"INT32_C",
"INT64_C",
"INTMAX_C",
"UINT8_C",
"UINT16_C",
"UINT32_C",
"UINT64_C",
"UINTMAX_C",
// C reserved keywords
"auto",
"break",
"case",
"char",
"const",
"continue",
"default",
"do",
"double",
"else",
"enum",
"extern",
"float",
"for",
"goto",
"if",
"inline", // Since C99
"int",
"long",
"register",
"restrict", // Since C99
"return",
"short",
"signed",
"sizeof",
"static",
"struct",
"switch",
"typedef",
"union",
"unsigned",
"volatile",
"while",
"_Alignas", // Since C11
"_Alignof", // Since C11
"_Atomic", // Since C11
"_Bool", // Since C99
"_Complex", // Since C99
"_Decimal128", // Since C23
"_Decimal32", // Since C23
"_Decimal64", // Since C23
"_Generic", // Since C11
"_Imaginary", // Since C99
"_Noreturn", // Since C11
"_Static_assert", // Since C11
"_Thread_local", // Since C11
// Convenience macros
"alignas",
"alignof",
"bool",
"complex",
"imaginary",
"noreturn",
"static_assert",
"thread_local",
// Convenience macros for atomic types
"atomic_bool",
"atomic_char",
"atomic_schar",
"atomic_uchar",
"atomic_short",
"atomic_ushort",
"atomic_int",
"atomic_uint",
"atomic_long",
"atomic_ulong",
"atomic_llong",
"atomic_ullong",
"atomic_char16_t",
"atomic_char32_t",
"atomic_wchar_t",
"atomic_int_least8_t",
"atomic_uint_least8_t",
"atomic_int_least16_t",
"atomic_uint_least16_t",
"atomic_int_least32_t",
"atomic_uint_least32_t",
"atomic_int_least64_t",
"atomic_uint_least64_t",
"atomic_int_fast8_t",
"atomic_uint_fast8_t",
"atomic_int_fast16_t",
"atomic_uint_fast16_t",
"atomic_int_fast32_t",
"atomic_uint_fast32_t",
"atomic_int_fast64_t",
"atomic_uint_fast64_t",
"atomic_intptr_t",
"atomic_uintptr_t",
"atomic_size_t",
"atomic_ptrdiff_t",
"atomic_intmax_t",
"atomic_uintmax_t",
// C Extensions
"_Pragma",
"asm",
};
static llvm::cl::opt<uint64_t> ModelTypeIDSeed("model-type-id-seed",
llvm::cl::desc("Set the seed "
"for the "
"generation of "
"ID of model "
"Types"),
llvm::cl::cat(MainCategory),
llvm::cl::init(false));
class RNG {
std::mt19937_64 Generator;
std::uniform_int_distribution<uint64_t> Distribution;
public:
RNG() :
Generator(ModelTypeIDSeed.getNumOccurrences() ? ModelTypeIDSeed.getValue() :
std::random_device()()),
Distribution(std::numeric_limits<uint64_t>::min(),
std::numeric_limits<uint64_t>::max()) {}
uint64_t get() { return Distribution(Generator); }
};
static llvm::ManagedStatic<RNG> IDGenerator;
model::Type::Type() :
model::generated::Type(model::TypeKind::Invalid, IDGenerator->get()){};
model::Type::Type(TypeKind::Values TK) :
model::Type::Type(TK, IDGenerator->get()) {
}
llvm::SmallVector<model::QualifiedType, 4> model::Type::edges() {
llvm::SmallVector<model::QualifiedType, 4> Empty;
auto *This = this;
auto GetEdges = [](auto &Upcasted) { return Upcasted.edges(); };
return upcast(This, GetEdges, Empty);
}
template<size_t I = 0>
model::UpcastableType
makeTypeWithIDImpl(model::TypeKind::Values Kind, uint64_t ID) {
using concrete_types = concrete_types_traits_t<model::Type>;
if constexpr (I < std::tuple_size_v<concrete_types>) {
using type = std::tuple_element_t<I, concrete_types>;
if (type::classof(typename type::Key(Kind, ID)))
return UpcastableType(new type(type::AssociatedKind, ID));
else
return model::makeTypeWithIDImpl<I + 1>(Kind, ID);
} else {
return UpcastableType(nullptr);
}
}
model::UpcastableType
makeTypeWithID(model::TypeKind::Values Kind, uint64_t ID) {
return makeTypeWithIDImpl(Kind, ID);
}
Identifier model::UnionField::name() const {
Identifier Result;
if (CustomName.empty())
(Twine("unnamed_field_") + Twine(Index)).toVector(Result);
else
Result = CustomName;
return Result;
}
Identifier model::StructField::name() const {
Identifier Result;
if (CustomName.empty())
(Twine("unnamed_field_at_offset_") + Twine(Offset)).toVector(Result);
else
Result = CustomName;
return Result;
}
Identifier model::Argument::name() const {
Identifier Result;
if (CustomName.empty())
(Twine("unnamed_arg_") + Twine(Index)).toVector(Result);
else
Result = CustomName;
return Result;
}
Identifier model::Type::name() const {
auto *This = this;
auto GetName = [](auto &Upcasted) -> Identifier { return Upcasted.name(); };
return upcast(This, GetName, Identifier(""));
}
void Qualifier::dump() const {
serialize(dbg, *this);
}
bool Qualifier::verify() const {
return verify(false);
}
bool Qualifier::verify(bool Assert) const {
VerifyHelper VH(Assert);
return verify(VH);
}
bool Qualifier::verify(VerifyHelper &VH) const {
switch (Kind) {
case QualifierKind::Invalid:
return VH.fail("Invalid qualifier found", *this);
case QualifierKind::Pointer:
return VH.maybeFail(Size > 0 and llvm::isPowerOf2_64(Size),
"Pointer qualifier size is not a power of 2",
*this);
case QualifierKind::Const:
return VH.maybeFail(Size == 0, "const qualifier has non-0 size", *this);
case QualifierKind::Array:
return VH.maybeFail(Size > 0, "Array qualifier size is 0");
default:
revng_abort();
}
return VH.fail();
}
static constexpr bool
isValidPrimitiveSize(PrimitiveTypeKind::Values PrimKind, uint8_t BS) {
switch (PrimKind) {
case PrimitiveTypeKind::Invalid:
return false;
case PrimitiveTypeKind::Void:
return BS == 0;
case PrimitiveTypeKind::Generic:
case PrimitiveTypeKind::PointerOrNumber:
case PrimitiveTypeKind::Number:
case PrimitiveTypeKind::Unsigned:
case PrimitiveTypeKind::Signed:
return BS == 1 or BS == 2 or BS == 4 or BS == 8 or BS == 16;
case PrimitiveTypeKind::Float:
return BS == 2 or BS == 4 or BS == 8 or BS == 12 or BS == 16;
default:
revng_abort();
}
revng_abort();
}
Identifier model::PrimitiveType::name() const {
Identifier Result;
switch (PrimitiveKind) {
case PrimitiveTypeKind::Void:
Result = "void";
break;
case PrimitiveTypeKind::Unsigned:
(Twine("uint") + Twine(Size * 8) + Twine("_t")).toVector(Result);
break;
case PrimitiveTypeKind::Number:
(Twine("number") + Twine(Size * 8) + Twine("_t")).toVector(Result);
break;
case PrimitiveTypeKind::PointerOrNumber:
("pointer_or_number" + Twine(Size * 8) + "_t").toVector(Result);
break;
case PrimitiveTypeKind::Generic:
(Twine("generic") + Twine(Size * 8) + Twine("_t")).toVector(Result);
break;
case PrimitiveTypeKind::Signed:
(Twine("int") + Twine(Size * 8) + Twine("_t")).toVector(Result);
break;
case PrimitiveTypeKind::Float:
(Twine("float") + Twine(Size * 8) + Twine("_t")).toVector(Result);
break;
default:
revng_abort();
}
return Result;
}
template<typename T>
Identifier customNameOrAutomatic(T *This) {
if (not This->CustomName.empty())
return This->CustomName;
else
return Identifier((Twine(T::AutomaticNamePrefix) + Twine(This->ID)).str());
}
Identifier model::StructType::name() const {
return customNameOrAutomatic(this);
}
Identifier model::TypedefType::name() const {
return customNameOrAutomatic(this);
}
Identifier model::EnumType::name() const {
return customNameOrAutomatic(this);
}
Identifier model::UnionType::name() const {
return customNameOrAutomatic(this);
}
Identifier model::NamedTypedRegister::name() const {
if (not CustomName.empty())
return CustomName;
else
return Identifier(model::Register::getRegisterName(Location));
}
Identifier model::RawFunctionType::name() const {
return customNameOrAutomatic(this);
}
Identifier model::CABIFunctionType::name() const {
return customNameOrAutomatic(this);
}
static uint64_t
makePrimitiveID(PrimitiveTypeKind::Values PrimitiveKind, uint8_t Size) {
return (static_cast<uint8_t>(PrimitiveKind) << 8) | Size;
}
static PrimitiveTypeKind::Values getPrimitiveKind(uint64_t ID) {
return static_cast<PrimitiveTypeKind::Values>(ID >> 8);
}
static uint8_t getPrimitiveSize(uint64_t ID) {
return ID & ((1 << 8) - 1);
}
PrimitiveType::PrimitiveType(PrimitiveTypeKind::Values PrimitiveKind,
uint8_t Size) :
PrimitiveType(AssociatedKind,
makePrimitiveID(PrimitiveKind, Size),
{},
"",
PrimitiveKind,
Size) {
}
PrimitiveType::PrimitiveType(uint64_t ID) :
PrimitiveType(AssociatedKind,
ID,
{},
"",
getPrimitiveKind(ID),
getPrimitiveSize(ID)) {
}
static bool beginsWithReservedPrefix(llvm::StringRef Name) {
return Name.startswith("unnamed_");
}
void EnumEntry::dump() const {
serialize(dbg, *this);
}
bool EnumEntry::verify() const {
return verify(false);
}
bool EnumEntry::verify(bool Assert) const {
VerifyHelper VH(Assert);
return verify(VH);
}
bool EnumEntry::verify(VerifyHelper &VH) const {
return VH.maybeFail(CustomName.verify(VH));
}
static bool isOnlyConstQualified(const QualifiedType &QT) {
if (QT.Qualifiers.empty() or QT.Qualifiers.size() > 1)
return false;
return QT.Qualifiers[0].isConstQualifier();
}
struct VoidConstResult {
bool IsVoid;
bool IsConst;
};
static VoidConstResult isVoidConst(const QualifiedType *QualType) {
VoidConstResult Result{ /* IsVoid */ false, /* IsConst */ false };
bool Done = false;
while (not Done) {
// If the argument type is qualified try to get the unqualified version.
// Warning: we only skip const-qualifiers here, cause the other qualifiers
// actually produce a different type.
const Type *UnqualType = nullptr;
if (not QualType->Qualifiers.empty()) {
// If it has a non-const qualifier, it can never be void because it's a
// pointer or array, so we can break out.
if (not isOnlyConstQualified(*QualType)) {
Done = true;
continue;
}
// We know that it's const-qualified here, and it only has one
// qualifier, hence we can skip the const-qualifier.
Result.IsConst = true;
return Result;
}
UnqualType = QualType->UnqualifiedType.get();
switch (UnqualType->Kind) {
// If we still have a typedef in our way, unwrap it and keep looking.
case TypeKind::Typedef: {
QualType = &cast<TypedefType>(UnqualType)->UnderlyingType;
} break;
// If we have a primitive type, check the name, and we're done.
case TypeKind::Primitive: {
auto *P = cast<PrimitiveType>(UnqualType);
Result.IsVoid = P->PrimitiveKind == PrimitiveTypeKind::Void;
Done = true;
} break;
// In all the other cases it's not void, break from the while.
default: {
Done = true;
} break;
}
}
return Result;
}
std::optional<uint64_t> QualifiedType::size() const {
VerifyHelper VH;
return size(VH);
}
RecursiveCoroutine<std::optional<uint64_t>>
QualifiedType::size(VerifyHelper &VH) const {
// This code assumes that the QualifiedType QT is well formed.
auto QIt = Qualifiers.begin();
auto QEnd = Qualifiers.end();
for (; QIt != QEnd; ++QIt) {
auto &Q = *QIt;
switch (Q.Kind) {
case QualifierKind::Invalid:
revng_abort();
case QualifierKind::Pointer:
// If we find a pointer, we're done
rc_return Q.Size;
case QualifierKind::Array: {
// The size is equal to (number of elements of the array) * (size of a
// single element).
const QualifiedType ArrayElem{ UnqualifiedType,
{ std::next(QIt), QEnd } };
auto MaybeSize = rc_recur ArrayElem.size(VH);
revng_assert(MaybeSize);
rc_return *MaybeSize *Q.Size;
}
case QualifierKind::Const:
// Do nothing, just skip over it
break;
default:
revng_abort();
}
}
rc_return rc_recur UnqualifiedType.get()->size(VH);
}
inline RecursiveCoroutine<bool>
isPrimitive(const model::QualifiedType &QT,
model::PrimitiveTypeKind::Values V) {
auto IsConstQualifier = [](const Qualifier &Q) {
return Q.Kind == model::QualifierKind::Const;
};
if (QT.Qualifiers.size() != 0
and not llvm::all_of(QT.Qualifiers, IsConstQualifier))
rc_return false;
const model::Type *UnqualifiedType = QT.UnqualifiedType.get();
if (auto *Primitive = llvm::dyn_cast<PrimitiveType>(UnqualifiedType))
rc_return Primitive->PrimitiveKind == V;
else if (auto *Typedef = llvm::dyn_cast<TypedefType>(UnqualifiedType))
rc_return rc_recur isPrimitive(Typedef->UnderlyingType, V);
rc_return false;
}
bool QualifiedType::isPrimitive(model::PrimitiveTypeKind::Values V) const {
return model::isPrimitive(*this, V);
}
std::optional<uint64_t> Type::size() const {
VerifyHelper VH;
return size(VH);
}
RecursiveCoroutine<std::optional<uint64_t>> Type::size(VerifyHelper &VH) const {
using ResultType = std::optional<uint64_t>;
auto MaybeSize = VH.size(this);
if (MaybeSize)
rc_return{ *MaybeSize == 0 ? ResultType{} : *MaybeSize };
// This code assumes that the type T is well formed.
ResultType Size;
switch (Kind) {
case TypeKind::Invalid:
revng_abort();
case TypeKind::RawFunctionType:
case TypeKind::CABIFunctionType:
// Function prototypes have no size
Size = {};
break;
case TypeKind::Primitive: {
auto *P = cast<PrimitiveType>(this);
if (P->PrimitiveKind == model::PrimitiveTypeKind::Void) {
// Void types have no size
revng_assert(P->Size == 0);
Size = {};
} else {
Size = P->Size;
}
} break;
case TypeKind::Enum: {
auto *U = llvm::cast<EnumType>(this)->UnderlyingType.get();
Size = rc_recur U->size(VH);
} break;
case TypeKind::Typedef: {
auto *Typedef = llvm::cast<TypedefType>(this);
Size = rc_recur Typedef->UnderlyingType.size(VH);
} break;
case TypeKind::Struct: {
Size = llvm::cast<StructType>(this)->Size;
} break;
case TypeKind::Union: {
auto *U = llvm::cast<UnionType>(this);
uint64_t Max = 0ULL;
for (const auto &Field : U->Fields) {
auto FieldSize = rc_recur Field.Type.size(VH);
Max = std::max(Max, FieldSize ? *FieldSize : 0);
}
Size = { Max == 0 ? ResultType{} : Max };
} break;
default:
revng_abort();
}
VH.setSize(this, Size ? *Size : 0);
rc_return Size;
};
static RecursiveCoroutine<bool>
verifyImpl(VerifyHelper &VH, const PrimitiveType *T) {
revng_assert(T->Kind == TypeKind::Primitive);
if (not T->CustomName.empty() or not T->OriginalName.empty())
rc_return VH.fail("PrimitiveTypes cannot have OriginalName or CustomName",
*T);
auto ExpectedID = makePrimitiveID(T->PrimitiveKind, T->Size);
if (T->ID != ExpectedID)
rc_return VH.fail(Twine("Wrong ID for PrimitiveType. Got: ") + Twine(T->ID)
+ ". Expected: " + Twine(ExpectedID) + ".",
*T);
if (not isValidPrimitiveSize(T->PrimitiveKind, T->Size))
rc_return VH.fail("Invalid PrimitiveType size: " + Twine(T->Size), *T);
rc_return true;
}
bool Identifier::verify() const {
return verify(false);
}
bool Identifier::verify(bool Assert) const {
VerifyHelper VH(Assert);
return verify(VH);
}
bool Identifier::verify(VerifyHelper &VH) const {
const auto AllAlphaNumOrUnderscore = [](const auto &Range) {
const auto IsNotUnderscore = [](const char C) { return C != '_'; };
return llvm::all_of(llvm::make_filter_range(Range, IsNotUnderscore),
isalnum);
};
return VH.maybeFail(not(not empty() and std::isdigit((*this)[0]))
and not startswith("_")
and AllAlphaNumOrUnderscore(*this)
and not beginsWithReservedPrefix(*this)
and not ReservedKeywords.count(llvm::StringRef(*this)),
Twine(*this) + " is not a valid identifier");
}
static RecursiveCoroutine<bool>
verifyImpl(VerifyHelper &VH, const EnumType *T) {
if (T->Kind != TypeKind::Enum or T->Entries.empty()
or not T->CustomName.verify(VH))
rc_return VH.fail();
// The underlying type has to be a primitive type
if (not T->UnderlyingType.isValid())
rc_return VH.fail();
auto *Underlying = dyn_cast<PrimitiveType>(T->UnderlyingType.get());
if (Underlying == nullptr)
rc_return VH.fail();
if (not rc_recur Underlying->verify(VH))
rc_return VH.fail();
// We only allow signed/unsigned as underlying type
if (Underlying->PrimitiveKind != PrimitiveTypeKind::Signed
and Underlying->PrimitiveKind != PrimitiveTypeKind::Unsigned)
rc_return VH.fail();
llvm::SmallSet<llvm::StringRef, 8> Names;
for (auto &Entry : T->Entries) {
if (not Entry.verify(VH))
rc_return VH.fail();
// TODO: verify Entry.Value is within boundaries
if (not Entry.CustomName.empty()) {
if (not Names.insert(Entry.CustomName).second)
rc_return VH.fail();
}
}
rc_return true;
}
static RecursiveCoroutine<bool>
verifyImpl(VerifyHelper &VH, const TypedefType *T) {
rc_return VH.maybeFail(T->CustomName.verify(VH)
and T->Kind == TypeKind::Typedef
and rc_recur T->UnderlyingType.verify(VH));
}
inline RecursiveCoroutine<bool> isScalar(const QualifiedType &QT) {
for (const Qualifier &Q : QT.Qualifiers) {
switch (Q.Kind) {
case QualifierKind::Invalid:
revng_abort();
case QualifierKind::Pointer:
rc_return true;
case QualifierKind::Array:
rc_return false;
case QualifierKind::Const:
break;
default:
revng_abort();
}
}
const Type *Unqualified = QT.UnqualifiedType.get();
revng_assert(Unqualified != nullptr);
if (llvm::isa<model::PrimitiveType>(Unqualified)) {
rc_return true;
} else if (auto *Typedef = llvm::dyn_cast<model::TypedefType>(Unqualified)) {
rc_return rc_recur isScalar(Typedef->UnderlyingType);
}
rc_return false;
}
bool model::QualifiedType::isScalar() const {
return ::model::isScalar(*this);
}
static RecursiveCoroutine<bool>
verifyImpl(VerifyHelper &VH, const StructType *T) {
using namespace llvm;
revng_assert(T->Kind == TypeKind::Struct);
if (not T->CustomName.verify(VH))
rc_return VH.fail("Invalid name", *T);
if (T->Size == 0)
rc_return VH.fail("Struct type has zero size", *T);
size_t Index = 0;
llvm::SmallSet<llvm::StringRef, 8> Names;
auto FieldIt = T->Fields.begin();
auto FieldEnd = T->Fields.end();
for (; FieldIt != FieldEnd; ++FieldIt) {
auto &Field = *FieldIt;
if (not rc_recur Field.verify(VH))
rc_return VH.fail("Can't verify type of field " + Twine(Index + 1), *T);
if (Field.Offset >= T->Size)
rc_return VH.fail("Field " + Twine(Index + 1)
+ " out of struct boundaries (offset: "
+ Twine(Field.Offset) + ", size: " + Twine(T->Size)
+ ")",
*T);
auto MaybeSize = rc_recur Field.Type.size(VH);
// This is verified AggregateField::verify
revng_assert(MaybeSize);
auto FieldEndOffset = Field.Offset + *MaybeSize;
auto NextFieldIt = std::next(FieldIt);
if (NextFieldIt != FieldEnd) {
// If this field is not the last, check that it does not overlap with the
// following field.
if (FieldEndOffset > NextFieldIt->Offset)
rc_return VH.fail("Field " + Twine(Index + 1)
+ " overlaps with the next one",
*T);
} else if (FieldEndOffset > T->Size) {
// Otherwise, if this field is the last, check that it's not larger than
// size.
rc_return VH.fail("Last field ends outside the struct", *T);
}
if (isVoidConst(&Field.Type).IsVoid)
rc_return VH.fail("Field " + Twine(Index + 1) + " is void", *T);
if (not Field.CustomName.empty()
and not Names.insert(Field.CustomName).second)
rc_return VH.fail("Collision in struct fields names", *T);
++Index;
}
rc_return true;
}
static RecursiveCoroutine<bool>
verifyImpl(VerifyHelper &VH, const UnionType *T) {
revng_assert(T->Kind == TypeKind::Union);
if (not T->CustomName.verify(VH))
rc_return VH.fail("Invalid name", *T);
if (T->Fields.empty())
rc_return VH.fail("Union type has zero fields", *T);
llvm::SmallSet<llvm::StringRef, 8> Names;
for (auto &Group : llvm::enumerate(T->Fields)) {
auto &Field = Group.value();
uint64_t ExpectedIndex = Group.index();
if (Field.Index != ExpectedIndex) {
rc_return VH.fail(Twine("Union type is missing field ")
+ Twine(ExpectedIndex),
*T);
}
if (not rc_recur Field.verify(VH))
rc_return VH.fail();
auto MaybeSize = rc_recur Field.Type.size(VH);
// This is verified AggregateField::verify
revng_assert(MaybeSize);
if (isVoidConst(&Field.Type).IsVoid) {
rc_return VH.fail("Field " + Twine(Field.Index) + " is void", *T);
}
if (not Field.CustomName.empty()
and not Names.insert(Field.CustomName).second)
rc_return VH.fail("Collision in union fields names", *T);
}
rc_return true;
}
static RecursiveCoroutine<bool>
verifyImpl(VerifyHelper &VH, const CABIFunctionType *T) {
if (not T->CustomName.verify(VH) or T->Kind != TypeKind::CABIFunctionType
or not rc_recur T->ReturnType.verify(VH))
rc_return VH.fail();
if (T->ABI == model::ABI::Invalid)
rc_return VH.fail();
for (auto &Group : llvm::enumerate(T->Arguments)) {
auto &Argument = Group.value();
uint64_t ArgPos = Group.index();
if (not Argument.CustomName.verify(VH))
rc_return VH.fail();
if (Argument.Index != ArgPos)
rc_return VH.fail();
if (not rc_recur Argument.Type.verify(VH))
rc_return VH.fail();
VoidConstResult VoidConst = isVoidConst(&Argument.Type);
if (VoidConst.IsVoid) {
// If we have a void argument it must be the only one, and the function
// cannot be vararg.
if (T->Arguments.size() > 1)
rc_return VH.fail();
// Cannot have const-qualified void as argument.
if (VoidConst.IsConst)
rc_return VH.fail();
}
}
rc_return true;
}
static RecursiveCoroutine<bool>
verifyImpl(VerifyHelper &VH, const RawFunctionType *T) {
for (const NamedTypedRegister &Argument : T->Arguments)
if (not rc_recur Argument.verify(VH))
rc_return VH.fail();
for (const TypedRegister &Return : T->ReturnValues)
if (not rc_recur Return.verify(VH))
rc_return VH.fail();
for (const Register::Values &Preserved : T->PreservedRegisters)
if (Preserved == Register::Invalid)
rc_return VH.fail();
rc_return VH.maybeFail(T->CustomName.verify(VH));
}
void Type::dump() const {
auto *This = this;
auto Dump = [](auto &Upcasted) { serialize(dbg, Upcasted); };
upcast(This, Dump);
}
bool Type::verify() const {
return verify(false);
}
bool Type::verify(bool Assert) const {
VerifyHelper VH(Assert);
return verify(VH);
}
RecursiveCoroutine<bool> Type::verify(VerifyHelper &VH) const {
if (VH.isVerified(this))
rc_return true;
// Ensure we have not infinite recursion
if (VH.isVerificationInProgess(this))
rc_return VH.fail();
VH.verificationInProgess(this);
if (ID == 0)
rc_return VH.fail();
bool Result = false;
// We could use upcast() but we'd need to workaround coroutines.
switch (Kind) {
case TypeKind::Primitive:
Result = rc_recur verifyImpl(VH, cast<PrimitiveType>(this));
break;
case TypeKind::Enum:
Result = rc_recur verifyImpl(VH, cast<EnumType>(this));
break;
case TypeKind::Typedef:
Result = rc_recur verifyImpl(VH, cast<TypedefType>(this));
break;
case TypeKind::Struct:
Result = rc_recur verifyImpl(VH, cast<StructType>(this));
break;
case TypeKind::Union:
Result = rc_recur verifyImpl(VH, cast<UnionType>(this));
break;
case TypeKind::CABIFunctionType:
Result = rc_recur verifyImpl(VH, cast<CABIFunctionType>(this));
break;
case TypeKind::RawFunctionType:
Result = rc_recur verifyImpl(VH, cast<RawFunctionType>(this));
break;
default: // Do nothing;
;
}
if (Result)
VH.setVerified(this);
VH.verificationCompleted(this);
rc_return VH.maybeFail(Result);
}
void QualifiedType::dump() const {
serialize(dbg, *this);
}
bool QualifiedType::verify() const {
return verify(false);
}
bool QualifiedType::verify(bool Assert) const {
VerifyHelper VH(Assert);
return verify(VH);
}
RecursiveCoroutine<bool> QualifiedType::verify(VerifyHelper &VH) const {
if (not UnqualifiedType.isValid())
rc_return VH.fail("Underlying type is invalid", *this);
// Verify the qualifiers are valid
for (const auto &Q : Qualifiers)
if (not Q.verify(VH))
rc_return VH.fail("Invalid qualifier", Q);
auto QIt = Qualifiers.begin();
auto QEnd = Qualifiers.end();
for (; QIt != QEnd; ++QIt) {
const auto &Q = *QIt;
auto NextQIt = std::next(QIt);
bool HasNext = NextQIt != QEnd;
// Check that we have not two consecutive const qualifiers
if (HasNext and Q.isConstQualifier() and NextQIt->isConstQualifier())
rc_return VH.fail("QualifiedType has two consecutive const qualifiers",
*this);
if (Q.isPointerQualifier()) {
// Don't proceed the verification, just make sure the pointer is either
// 32- or 64-bits
rc_return VH.maybeFail(Q.Size == 4 or Q.Size == 8,
"Only 32-bit and 64-bit pointers are currently "
"supported",
*this);
} else if (Q.isArrayQualifier()) {
// Ensure there's at least one element
if (Q.Size < 1)
rc_return VH.fail("Arrays need to have at least an element", *this);
// Verify element type
QualifiedType ElementType{ UnqualifiedType, { NextQIt, QEnd } };
if (not rc_recur ElementType.verify(VH))
rc_return VH.fail("Array element invalid", ElementType);
// Ensure the element type has a size and stop
auto MaybeSize = rc_recur ElementType.size(VH);
rc_return VH.maybeFail(MaybeSize.has_value(),
"Cannot compute array size",
ElementType);
} else if (Q.isConstQualifier()) {
// const qualifiers must have zero size
if (Q.Size != 0)
rc_return VH.fail("const qualifier has non-0 size");
} else {
revng_abort();
}
}
// If we get here, we either have no qualifiers or just const qualifiers:
// recur on the underlying type
rc_return VH.maybeFail(rc_recur UnqualifiedType.get()->verify(VH));
}
template<typename T>
RecursiveCoroutine<bool>
verifyTypedRegisterCommon(const T &TypedRegister, VerifyHelper &VH) {
// Ensure the type we're pointing to is scalar
if (not isScalar(TypedRegister->Type))
rc_return VH.fail();
if (TypedRegister->Location == Register::Invalid)
rc_return VH.fail();
// Ensure if fits in the corresponding register
auto MaybeTypeSize = rc_recur TypedRegister->Type.size(VH);
// Zero-sized types are not allowed
if (not MaybeTypeSize)
rc_return VH.fail();
size_t RegisterSize = model::Register::getSize(TypedRegister->Location);
if (*MaybeTypeSize > RegisterSize)
rc_return VH.fail();
rc_return VH.maybeFail(rc_recur TypedRegister->Type.verify(VH));
}
void TypedRegister::dump() const {
serialize(dbg, *this);
}
bool TypedRegister::verify() const {
return verify(false);
}
bool TypedRegister::verify(bool Assert) const {
VerifyHelper VH(Assert);
return verify(VH);
}
RecursiveCoroutine<bool> TypedRegister::verify(VerifyHelper &VH) const {
rc_return verifyTypedRegisterCommon(this, VH);
}
void NamedTypedRegister::dump() const {
serialize(dbg, *this);
}
bool NamedTypedRegister::verify() const {
return verify(false);
}
bool NamedTypedRegister::verify(bool Assert) const {
VerifyHelper VH(Assert);
return verify(VH);
}
RecursiveCoroutine<bool> NamedTypedRegister::verify(VerifyHelper &VH) const {
// Ensure the name is valid
if (not CustomName.verify(VH))
rc_return VH.fail();
rc_return verifyTypedRegisterCommon(this, VH);
}
bool StructField::verify() const {
return verify(false);
}
bool StructField::verify(bool Assert) const {
VerifyHelper VH(Assert);
return verify(VH);
}
RecursiveCoroutine<bool> StructField::verify(VerifyHelper &VH) const {
if (not rc_recur Type.verify(VH))
rc_return VH.fail("Aggregate field type is not valid");
// Aggregated fields cannot be zero-sized fields
auto MaybeSize = rc_recur Type.size(VH);
if (not MaybeSize)
rc_return VH.fail("Aggregate field is zero-sized");
rc_return VH.maybeFail(CustomName.verify(VH));
}
bool UnionField::verify() const {
return verify(false);
}
bool UnionField::verify(bool Assert) const {
VerifyHelper VH(Assert);
return verify(VH);
}
RecursiveCoroutine<bool> UnionField::verify(VerifyHelper &VH) const {
if (not rc_recur Type.verify(VH))
rc_return VH.fail("Aggregate field type is not valid");
// Aggregated fields cannot be zero-sized fields
auto MaybeSize = rc_recur Type.size(VH);
if (not MaybeSize)
rc_return VH.fail("Aggregate field is zero-sized");
rc_return VH.maybeFail(CustomName.verify(VH));
}
void Argument::dump() const {
serialize(dbg, *this);
}
bool Argument::verify() const {
return verify(false);
}
bool Argument::verify(bool Assert) const {
VerifyHelper VH(Assert);
return verify(VH);
}
RecursiveCoroutine<bool> Argument::verify(VerifyHelper &VH) const {
rc_return VH.maybeFail(CustomName.verify(VH) and rc_recur Type.verify(VH));
}
} // namespace model