mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
df30792435
This commit completes the support of various rare PrimitiveTypes, that has been added opportunistically over time and has remained inconsistent across the codebase: - float80_t - float96_t - generic80_t - generic96_t The first two are necessary because on some platforms long double is either 10 or 12 bytes wide. The second two are necessary because the Generic PrimitiveType should allow all non-zero byte sizes allowed by any other PrimitiveType.
632 lines
22 KiB
C++
632 lines
22 KiB
C++
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
#include <bit>
|
|
|
|
#define BOOST_TEST_MODULE ModelType
|
|
bool init_unit_test();
|
|
#include "boost/test/unit_test.hpp"
|
|
|
|
#include "revng/Model/ABI.h"
|
|
#include "revng/Model/Binary.h"
|
|
#include "revng/Model/Types.h"
|
|
|
|
using namespace model;
|
|
|
|
using llvm::cast;
|
|
using llvm::Twine;
|
|
|
|
using model::PrimitiveTypeKind::Signed;
|
|
using model::PrimitiveTypeKind::Void;
|
|
|
|
static TupleTree<model::Binary>
|
|
serializeDeserialize(const TupleTree<model::Binary> &T) {
|
|
|
|
std::string Buffer;
|
|
T.serialize(Buffer);
|
|
llvm::dbgs() << "Serialized\n" << Buffer;
|
|
|
|
auto Deserialized = TupleTree<model::Binary>::deserialize(Buffer);
|
|
|
|
std::string OtherBuffer;
|
|
Deserialized->serialize(OtherBuffer);
|
|
llvm::dbgs() << "Deserialized\n" << OtherBuffer;
|
|
|
|
return std::move(Deserialized.get());
|
|
}
|
|
|
|
static bool checkSerialization(const TupleTree<model::Binary> &T) {
|
|
revng_check(T->verify(true));
|
|
auto Deserialized = serializeDeserialize(T);
|
|
revng_check(Deserialized->verify(true));
|
|
return T->Types == Deserialized->Types;
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(PrimitiveTypes) {
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Void, 0).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Unsigned, 1).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Unsigned, 2).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Unsigned, 4).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Unsigned, 8).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Unsigned, 16).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Signed, 1).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Signed, 2).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Signed, 4).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Signed, 8).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Signed, 16).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Float, 2).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Float, 4).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Float, 8).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Float, 10).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Float, 12).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Float, 16).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Generic, 1).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Generic, 2).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Generic, 4).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Generic, 8).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Generic, 10).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Generic, 12).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Generic, 16).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Number, 1).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Number, 2).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Number, 4).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Number, 8).verify(true));
|
|
revng_check(PrimitiveType(PrimitiveTypeKind::Number, 16).verify(true));
|
|
auto PointerOrNumberKind = PrimitiveTypeKind::PointerOrNumber;
|
|
revng_check(PrimitiveType(PointerOrNumberKind, 1).verify(true));
|
|
revng_check(PrimitiveType(PointerOrNumberKind, 2).verify(true));
|
|
revng_check(PrimitiveType(PointerOrNumberKind, 4).verify(true));
|
|
revng_check(PrimitiveType(PointerOrNumberKind, 8).verify(true));
|
|
revng_check(PrimitiveType(PointerOrNumberKind, 16).verify(true));
|
|
|
|
for (uint8_t ByteSize = 0; ByteSize < 20; ++ByteSize) {
|
|
|
|
using namespace std::string_literals;
|
|
|
|
auto Unsigned = PrimitiveType(PrimitiveTypeKind::Unsigned, ByteSize);
|
|
auto Signed = PrimitiveType(PrimitiveTypeKind::Signed, ByteSize);
|
|
auto Number = PrimitiveType(PrimitiveTypeKind::Number, ByteSize);
|
|
auto PointerOrNumber = PrimitiveType(PointerOrNumberKind, ByteSize);
|
|
|
|
if (std::has_single_bit(ByteSize)) {
|
|
revng_check(Signed.verify(true));
|
|
revng_check(Unsigned.verify(true));
|
|
revng_check(Number.verify(true));
|
|
revng_check(PointerOrNumber.verify(true));
|
|
auto ExpectedName = ("uint" + Twine(8 * ByteSize) + "_t").str();
|
|
revng_check(Unsigned.name() == ExpectedName);
|
|
ExpectedName = ("int" + Twine(8 * ByteSize) + "_t").str();
|
|
revng_check(Signed.name() == ExpectedName);
|
|
ExpectedName = ("number" + Twine(8 * ByteSize) + "_t").str();
|
|
revng_check(Number.name() == ExpectedName);
|
|
ExpectedName = ("pointer_or_number" + Twine(8 * ByteSize) + "_t").str();
|
|
revng_check(PointerOrNumber.name() == ExpectedName);
|
|
} else {
|
|
revng_check(not Signed.verify(false));
|
|
revng_check(not Unsigned.verify(false));
|
|
revng_check(not Number.verify(false));
|
|
revng_check(not PointerOrNumber.verify(false));
|
|
}
|
|
}
|
|
|
|
for (uint8_t ByteSize = 0; ByteSize < 20; ++ByteSize) {
|
|
using namespace std::string_literals;
|
|
|
|
auto Float = PrimitiveType(PrimitiveTypeKind::Float, ByteSize);
|
|
auto G = PrimitiveType(PrimitiveTypeKind::Generic, ByteSize);
|
|
if (ByteSize == 2 or ByteSize == 4 or ByteSize == 8 or ByteSize == 10
|
|
or ByteSize == 12 or ByteSize == 16) {
|
|
revng_check(Float.verify(true));
|
|
revng_check(Float.name() == ("float" + Twine(8 * ByteSize) + "_t").str());
|
|
revng_check(G.verify(true));
|
|
revng_check(G.name() == ("generic" + Twine(8 * ByteSize) + "_t").str());
|
|
} else {
|
|
revng_check(not Float.verify(false));
|
|
if (ByteSize == 1)
|
|
revng_check(G.verify(true));
|
|
else
|
|
revng_check(not G.verify(false));
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(EnumTypes) {
|
|
revng_check(not EnumType().verify(false));
|
|
|
|
TupleTree<model::Binary> T;
|
|
|
|
auto Int32 = T->getPrimitiveType(Signed, 4);
|
|
|
|
TypePath EnumPath = T->recordNewType(makeType<EnumType>());
|
|
auto *Enum = cast<EnumType>(EnumPath.get());
|
|
revng_check(T->Types.size() == 2);
|
|
|
|
// The enum does not verify if we don't define a valid underlying type and
|
|
// at least one enum entry
|
|
auto Int32QT = model::QualifiedType(Int32, {});
|
|
Enum->UnderlyingType = Int32QT;
|
|
revng_check(not Enum->verify(false));
|
|
revng_check(not T->verify(false));
|
|
|
|
// With a valid underlying type and at least one entry we're good, but we
|
|
// have to initialize all the cross references in the tree.
|
|
EnumEntry Entry = EnumEntry{ 0 };
|
|
Entry.CustomName = "value0";
|
|
revng_check(Entry.verify(true));
|
|
|
|
revng_check(Enum->Entries.insert(Entry).second);
|
|
revng_check(Enum->verify(true));
|
|
revng_check(T->verify(true));
|
|
revng_check(checkSerialization(T));
|
|
|
|
// We cannot insert other entries with the same value, but we can insert new
|
|
// entries with different values.
|
|
revng_check(Enum->Entries.size() == 1);
|
|
revng_check(not Enum->Entries.insert(EnumEntry{ 0 }).second);
|
|
revng_check(Enum->Entries.size() == 1);
|
|
revng_check(Enum->verify(true));
|
|
revng_check(T->verify(true));
|
|
revng_check(checkSerialization(T));
|
|
|
|
revng_check(Enum->verify(true));
|
|
revng_check(T->verify(true));
|
|
revng_check(Enum->Entries.insert(EnumEntry{ 1 }).second);
|
|
revng_check(Enum->Entries.size() == 2);
|
|
revng_check(Enum->verify(true));
|
|
revng_check(T->verify(true));
|
|
revng_check(checkSerialization(T));
|
|
|
|
// Inserting two entries with the same name succceds but it's bad.
|
|
EnumEntry Entry1{ 5 };
|
|
Entry1.CustomName = "some_value";
|
|
revng_check(Enum->Entries.insert(Entry1).second);
|
|
revng_check(Enum->Entries.size() == 3);
|
|
revng_check(Enum->verify(true));
|
|
revng_check(T->verify(true));
|
|
revng_check(checkSerialization(T));
|
|
EnumEntry Entry2{ 7 };
|
|
Entry2.CustomName = "some_value";
|
|
revng_check(Enum->Entries.insert(Entry2).second);
|
|
revng_check(Enum->Entries.size() == 4);
|
|
revng_check(not Enum->verify(false));
|
|
revng_check(not T->verify(false));
|
|
// But if we remove the dupicated entry we're good again
|
|
revng_check(Enum->Entries.erase(7));
|
|
revng_check(Enum->Entries.size() == 3);
|
|
revng_check(Enum->verify(true));
|
|
revng_check(T->verify(true));
|
|
revng_check(checkSerialization(T));
|
|
|
|
// But if we break the underlying, making it point to a type that does not
|
|
// exist, we're not good anymore
|
|
auto BrokenPath = TypePath::fromString(T.get(), "/Types/TypedefType-42");
|
|
Enum->UnderlyingType = { BrokenPath, {} };
|
|
revng_check(not Enum->verify(false));
|
|
revng_check(not T->verify(false));
|
|
|
|
// Also we set the underlying type to a valid type, but that is not a
|
|
// primitive integer type, we are not good
|
|
auto PathToNonInt = T->getTypePath(Enum);
|
|
Enum->UnderlyingType = { PathToNonInt, {} };
|
|
revng_check(not Enum->verify(false));
|
|
revng_check(not T->verify(false));
|
|
|
|
// If we put back the proper underlying type it verifies.
|
|
Enum->UnderlyingType = Int32QT;
|
|
revng_check(Enum->verify(true));
|
|
revng_check(T->verify(true));
|
|
revng_check(checkSerialization(T));
|
|
|
|
// But if we clear the entries it does not verify anymore
|
|
Enum->Entries.clear();
|
|
revng_check(not Enum->verify(false));
|
|
revng_check(not T->verify(false));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(TypedefTypes) {
|
|
TupleTree<model::Binary> T;
|
|
|
|
auto Int32 = T->getPrimitiveType(Signed, 4);
|
|
|
|
// Insert the typedef
|
|
|
|
TypePath TypedefPath = T->recordNewType(makeType<TypedefType>());
|
|
auto *Typedef = cast<TypedefType>(TypedefPath.get());
|
|
revng_check(T->Types.size() == 2);
|
|
|
|
// The pid_t typedef refers to the int32_t
|
|
Typedef->UnderlyingType = { Int32, {} };
|
|
revng_check(Typedef->verify(true));
|
|
revng_check(T->verify(true));
|
|
revng_check(checkSerialization(T));
|
|
|
|
// Adding qualifiers the typedef still verifies
|
|
Typedef->UnderlyingType.Qualifiers.push_back(Qualifier::createConst());
|
|
revng_check(Typedef->verify(true));
|
|
revng_check(T->verify(true));
|
|
Typedef->UnderlyingType.Qualifiers.push_back(Qualifier::createArray(42));
|
|
revng_check(Typedef->verify(true));
|
|
revng_check(T->verify(true));
|
|
Typedef->UnderlyingType.Qualifiers.push_back(Qualifier::createPointer(8));
|
|
revng_check(Typedef->verify(true));
|
|
revng_check(T->verify(true));
|
|
revng_check(checkSerialization(T));
|
|
|
|
// Removing qualifiers, the typedef still verifies
|
|
Typedef->UnderlyingType.Qualifiers.clear();
|
|
revng_check(Typedef->verify(true));
|
|
revng_check(T->verify(true));
|
|
revng_check(checkSerialization(T));
|
|
|
|
// If the underlying type is the type itself something is broken
|
|
Typedef->UnderlyingType.UnqualifiedType = T->getTypePath(Typedef);
|
|
revng_check(not Typedef->verify(false));
|
|
revng_check(not T->verify(false));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(StructTypes) {
|
|
revng_check(not StructType().verify(false));
|
|
|
|
TupleTree<model::Binary> T;
|
|
|
|
auto Int32 = T->getPrimitiveType(Signed, 4);
|
|
auto VoidT = T->getPrimitiveType(Void, 0);
|
|
|
|
// Insert the struct
|
|
TypePath StructPath = T->recordNewType(makeType<StructType>());
|
|
auto *Struct = cast<StructType>(StructPath.get());
|
|
revng_check(T->Types.size() == 3);
|
|
|
|
// Let's make it large, so that we can play around with fields.
|
|
Struct->Size = 1024;
|
|
|
|
// Insert field in the struct
|
|
StructField Field0 = StructField{ 0 };
|
|
Field0.Type = { Int32, {} };
|
|
revng_check(Struct->Fields.insert(Field0).second);
|
|
revng_check(Struct->verify(true));
|
|
revng_check(T->verify(true));
|
|
revng_check(checkSerialization(T));
|
|
|
|
// Adding a new field is valid
|
|
StructField Field1 = StructField{ 4 };
|
|
Field1.Type = { Int32, {} };
|
|
revng_check(Struct->Fields.insert(Field1).second);
|
|
revng_check(Struct->verify(true));
|
|
revng_check(T->verify(true));
|
|
revng_check(checkSerialization(T));
|
|
|
|
// Inserting fails if the index is already present
|
|
StructField Field1Bis = StructField{ 4 };
|
|
Field1Bis.Type = { Int32, {} };
|
|
revng_check(not Struct->Fields.insert(Field1Bis).second);
|
|
revng_check(Struct->verify(true));
|
|
revng_check(T->verify(true));
|
|
revng_check(checkSerialization(T));
|
|
|
|
// Assigning succeeds if even if an index is already present
|
|
StructField Field1Ter = StructField{ 4 };
|
|
Field1Ter.Type = { Int32, {} };
|
|
Field1Ter.CustomName = "fld1ter";
|
|
revng_check(not Struct->Fields.insert_or_assign(Field1Ter).second);
|
|
revng_check(Struct->verify(true));
|
|
revng_check(Struct->Fields.at(4).CustomName == "fld1ter");
|
|
revng_check(T->verify(true));
|
|
revng_check(checkSerialization(T));
|
|
|
|
// Adding a new field whose position is not consecutive to others builds a
|
|
// struct that is valid
|
|
StructField AnotherField = StructField{ 128 };
|
|
AnotherField.Type = { Int32, {} };
|
|
revng_check(Struct->Fields.insert(AnotherField).second);
|
|
revng_check(Struct->verify(true));
|
|
revng_check(T->verify(true));
|
|
revng_check(checkSerialization(T));
|
|
|
|
// Adding a new field that overlaps with another is not valid
|
|
StructField Overlap = StructField{ 129 };
|
|
Overlap.Type = { Int32, {} };
|
|
revng_check(Struct->Fields.insert(Overlap).second);
|
|
revng_check(not Struct->verify(false));
|
|
revng_check(not T->verify(false));
|
|
|
|
// Removing the overlapping field fixes the struct
|
|
revng_check(Struct->Fields.erase(129));
|
|
revng_check(Struct->verify(true));
|
|
revng_check(T->verify(true));
|
|
revng_check(checkSerialization(T));
|
|
|
|
// Erasing a field that's not there fails
|
|
revng_check(not Struct->Fields.erase(129));
|
|
revng_check(Struct->verify(true));
|
|
revng_check(T->verify(true));
|
|
revng_check(checkSerialization(T));
|
|
|
|
// Shrinking the size does not break the struct
|
|
Struct->Size = 132;
|
|
revng_check(Struct->verify(true));
|
|
revng_check(T->verify(true));
|
|
revng_check(checkSerialization(T));
|
|
|
|
for (int I = 0; I < 132; ++I) {
|
|
// But shrinking too much breaks it again
|
|
Struct->Size = I;
|
|
revng_check(not Struct->verify(false));
|
|
revng_check(not T->verify(false));
|
|
}
|
|
|
|
// Fixing the size fixes the struct
|
|
Struct->Size = 132;
|
|
revng_check(Struct->verify(true));
|
|
revng_check(T->verify(true));
|
|
revng_check(checkSerialization(T));
|
|
|
|
// Struct without fields are valid as long as their size is not zero
|
|
Struct->Fields.clear();
|
|
revng_check(Struct->verify(false));
|
|
revng_check(T->verify(false));
|
|
Struct->Size = 0;
|
|
revng_check(not Struct->verify(false));
|
|
revng_check(not T->verify(false));
|
|
|
|
// Put the size back to a large value for the other tests.
|
|
Struct->Size = 100;
|
|
revng_check(Struct->verify(false));
|
|
revng_check(T->verify(false));
|
|
|
|
// Struct x cannot have a field with type x
|
|
Struct->Fields.clear();
|
|
StructField Same = StructField{ 0 };
|
|
Same.Type = { T->getTypePath(Struct), {} };
|
|
revng_check(Struct->Fields.insert(Same).second);
|
|
revng_check(not Struct->verify(false));
|
|
revng_check(not T->verify(false));
|
|
|
|
// Adding a void field is not valid
|
|
Struct->Fields.clear();
|
|
StructField VoidField = StructField{ 0 };
|
|
VoidField.Type = { VoidT, {} };
|
|
revng_check(Struct->Fields.insert(VoidField).second);
|
|
revng_check(not Struct->verify(false));
|
|
revng_check(not T->verify(false));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(UnionTypes) {
|
|
revng_check(not UnionType().verify(false));
|
|
|
|
TupleTree<model::Binary> T;
|
|
|
|
auto Int32 = T->getPrimitiveType(Signed, 4);
|
|
auto Int64 = T->getPrimitiveType(Signed, 8);
|
|
auto VoidT = T->getPrimitiveType(Void, 0);
|
|
|
|
// Insert the union
|
|
TypePath UnionPath = T->recordNewType(makeType<UnionType>());
|
|
auto *Union = cast<UnionType>(UnionPath.get());
|
|
revng_check(T->Types.size() == 4);
|
|
|
|
// Insert field in the struct
|
|
UnionField Field0(0);
|
|
Field0.Type = { Int32, {} };
|
|
revng_check(Union->Fields.insert(Field0).second);
|
|
revng_check(Union->verify(true));
|
|
revng_check(T->verify(true));
|
|
revng_check(checkSerialization(T));
|
|
|
|
// Adding a new field is valid
|
|
{
|
|
UnionField Field1(1);
|
|
Field1.Type = { Int64, {} };
|
|
Field1.CustomName = "fld1";
|
|
const auto [It, New] = Union->Fields.insert(std::move(Field1));
|
|
revng_check(New);
|
|
}
|
|
revng_check(Union->verify(true));
|
|
revng_check(T->verify(true));
|
|
revng_check(checkSerialization(T));
|
|
|
|
{
|
|
// Assigning another field in a different position with a duplicated name
|
|
// succeeds, but verification fails.
|
|
UnionField Field1(2);
|
|
Field1.Type = { Int32, {} };
|
|
Field1.CustomName = "fld1";
|
|
const auto [It, New] = Union->Fields.insert(std::move(Field1));
|
|
revng_check(New);
|
|
revng_check(Union->Fields.at(It->Index).CustomName == "fld1");
|
|
revng_check(not Union->verify(false));
|
|
revng_check(not T->verify(false));
|
|
|
|
// But removing goes back to good again
|
|
revng_check(Union->Fields.erase(It->Index));
|
|
revng_check(Union->verify(true));
|
|
revng_check(T->verify(true));
|
|
revng_check(checkSerialization(T));
|
|
}
|
|
|
|
// Union without fields are invalid
|
|
Union->Fields.clear();
|
|
revng_check(not Union->verify(false));
|
|
revng_check(not T->verify(false));
|
|
|
|
// Union x cannot have a field with type x
|
|
Union->Fields.clear();
|
|
UnionField Same;
|
|
Same.Type = { T->getTypePath(Union), {} };
|
|
revng_check(Union->Fields.insert(Same).second);
|
|
revng_check(not Union->verify(false));
|
|
revng_check(not T->verify(false));
|
|
|
|
// Adding a void field is not valid
|
|
Union->Fields.clear();
|
|
UnionField VoidField;
|
|
VoidField.Type = { VoidT, {} };
|
|
revng_check(Union->Fields.insert(VoidField).second);
|
|
revng_check(not Union->verify(false));
|
|
revng_check(not T->verify(false));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(CABIFunctionTypes) {
|
|
TupleTree<model::Binary> T;
|
|
|
|
auto Int32 = T->getPrimitiveType(Signed, 4);
|
|
auto VoidT = T->getPrimitiveType(Void, 0);
|
|
|
|
// Create a C-like function type
|
|
TypePath FunctionPath = T->recordNewType(makeType<CABIFunctionType>());
|
|
auto *FunctionType = cast<CABIFunctionType>(FunctionPath.get());
|
|
FunctionType->ABI = model::ABI::SystemV_x86_64;
|
|
revng_check(T->Types.size() == 3);
|
|
|
|
revng_check(not FunctionType->size().has_value());
|
|
|
|
// Insert argument in the function type
|
|
Argument Arg0{ 0 };
|
|
Arg0.Type = { Int32, {} };
|
|
const auto &[InsertedArgIt, New] = FunctionType->Arguments.insert(Arg0);
|
|
revng_check(InsertedArgIt != FunctionType->Arguments.end());
|
|
revng_check(New);
|
|
|
|
// Verification fails due to missing return type
|
|
revng_check(not FunctionType->verify(false));
|
|
revng_check(not T->verify(false));
|
|
|
|
QualifiedType RetTy{ Int32, {} };
|
|
FunctionType->ReturnType = RetTy;
|
|
revng_check(FunctionType->verify(true));
|
|
revng_check(T->verify(true));
|
|
revng_check(checkSerialization(T));
|
|
|
|
// Adding a new field is valid, and we can have a function type with an
|
|
// argument of the same type of itself.
|
|
Argument Arg1{ 1 };
|
|
Arg1.Type = { Int32, {} };
|
|
revng_check(FunctionType->Arguments.insert(Arg1).second);
|
|
revng_check(FunctionType->verify(true));
|
|
revng_check(checkSerialization(T));
|
|
|
|
// Inserting an ArgumentType in a position that is already taken fails
|
|
Argument Arg1Bis{ 1 };
|
|
Arg1Bis.Type = { Int32, {} };
|
|
revng_check(not FunctionType->Arguments.insert(Arg1Bis).second);
|
|
revng_check(FunctionType->verify(true));
|
|
revng_check(T->verify(true));
|
|
revng_check(checkSerialization(T));
|
|
|
|
// Assigning an ArgumentType in a position that is already taken succeeds
|
|
revng_check(not FunctionType->Arguments.insert_or_assign(Arg1Bis).second);
|
|
revng_check(FunctionType->verify(true));
|
|
auto &ArgT = FunctionType->Arguments.at(1);
|
|
revng_check(ArgT.Type.UnqualifiedType == Int32);
|
|
revng_check(T->verify(true));
|
|
revng_check(checkSerialization(T));
|
|
|
|
// FunctionType without argument are valid
|
|
FunctionType->Arguments.clear();
|
|
revng_check(FunctionType->verify(true));
|
|
revng_check(T->verify(true));
|
|
revng_check(checkSerialization(T));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(RawFunctionTypes) {
|
|
TupleTree<model::Binary> T;
|
|
|
|
auto Primitive64 = T->getPrimitiveType(model::PrimitiveTypeKind::Generic, 4);
|
|
QualifiedType Generic64 = { Primitive64, {} };
|
|
|
|
auto RAFPointer = makeType<model::RawFunctionType>();
|
|
auto *RAF = cast<model::RawFunctionType>(RAFPointer.get());
|
|
|
|
revng_check(RAF->verify(true));
|
|
|
|
//
|
|
// Test non-scalar argument
|
|
//
|
|
{
|
|
model::TypedRegister RAXArgument(model::Register::rax_x86_64);
|
|
RAXArgument.Type = { Primitive64, { { QualifierKind::Array, 10 } } };
|
|
revng_check(not RAXArgument.verify(false));
|
|
}
|
|
|
|
//
|
|
// Add two arguments
|
|
//
|
|
{
|
|
model::NamedTypedRegister RDIArgument(model::Register::rdi_x86_64);
|
|
RDIArgument.Type = Generic64;
|
|
revng_check(RDIArgument.verify(true));
|
|
RAF->Arguments.insert(RDIArgument);
|
|
revng_check(RAF->verify(true));
|
|
|
|
model::NamedTypedRegister RSIArgument(model::Register::rsi_x86_64);
|
|
RSIArgument.Type = Generic64;
|
|
RSIArgument.CustomName = "Second";
|
|
revng_check(RSIArgument.verify(true));
|
|
RAF->Arguments.insert(RSIArgument);
|
|
revng_check(RAF->verify(true));
|
|
}
|
|
|
|
// Add a return value
|
|
{
|
|
model::TypedRegister RAXReturnValue(model::Register::rax_x86_64);
|
|
RAXReturnValue.Type = Generic64;
|
|
revng_check(RAXReturnValue.verify(true));
|
|
RAF->ReturnValues.insert(RAXReturnValue);
|
|
revng_check(RAF->verify(true));
|
|
}
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(QualifiedTypes) {
|
|
TupleTree<model::Binary> T;
|
|
auto Void = T->getPrimitiveType(model::PrimitiveTypeKind::Void, 0);
|
|
auto Generic64 = T->getPrimitiveType(model::PrimitiveTypeKind::Generic, 8);
|
|
|
|
revng_check(Void.get()->verify(true));
|
|
revng_check(not Void.get()->size().has_value());
|
|
|
|
revng_check(Generic64.get()->verify(true));
|
|
revng_check(*Generic64.get()->size() == 8);
|
|
|
|
QualifiedType VoidPointer = { Void,
|
|
{ { model::QualifierKind::Pointer, 4 } } };
|
|
revng_check(VoidPointer.verify(true));
|
|
|
|
model::Qualifier Pointer64Qualifier{ model::QualifierKind::Pointer, 8 };
|
|
QualifiedType Generic64Pointer = { Void, { Pointer64Qualifier } };
|
|
revng_check(Generic64Pointer.verify(true));
|
|
|
|
QualifiedType DoublePointer = { Void,
|
|
{ Pointer64Qualifier, Pointer64Qualifier } };
|
|
revng_check(DoublePointer.verify(true));
|
|
|
|
QualifiedType WeirdSizedPointer = {
|
|
Void, { { model::QualifierKind::Pointer, 7 } }
|
|
};
|
|
revng_check(not WeirdSizedPointer.verify(false));
|
|
|
|
model::Qualifier ConstQualifier{ model::QualifierKind::Const, 0 };
|
|
QualifiedType ConstVoid = { Void, { ConstQualifier } };
|
|
revng_check(ConstVoid.verify(true));
|
|
|
|
QualifiedType ConstConstVoid = { Void, { ConstQualifier, ConstQualifier } };
|
|
revng_check(not ConstConstVoid.verify(false));
|
|
|
|
QualifiedType ConstPointerConstVoid = {
|
|
Void, { ConstQualifier, Pointer64Qualifier, ConstQualifier }
|
|
};
|
|
revng_check(ConstPointerConstVoid.verify(true));
|
|
|
|
model::Qualifier TenElementsArray{ model::QualifierKind::Array, 10 };
|
|
QualifiedType VoidArray = { Void, { TenElementsArray } };
|
|
revng_check(not VoidArray.verify(false));
|
|
|
|
QualifiedType VoidPointerArray = { Void,
|
|
{ TenElementsArray, Pointer64Qualifier } };
|
|
revng_check(VoidPointerArray.verify(true));
|
|
|
|
model::Qualifier ZeroElementsArray{ model::QualifierKind::Array, 0 };
|
|
QualifiedType ZeroSizedVoidArray = { Void, { ZeroElementsArray } };
|
|
revng_check(not ZeroSizedVoidArray.verify(false));
|
|
}
|