Files
revng-revng/lib/Clift/CliftTypes.cpp
2026-06-15 17:28:22 +02:00

1763 lines
50 KiB
C++

//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include <optional>
#include <string>
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringExtras.h"
#include "mlir/IR/BuiltinAttributes.h"
#include "mlir/IR/MLIRContext.h"
#include "mlir/Support/LogicalResult.h"
#include "revng/Clift/CliftTypes.h"
#include "revng/Support/Identifier.h"
#include "CliftBytecode.h"
// keep this order
#include "revng/Clift/CliftAttributes.h"
#define GET_TYPEDEF_CLASSES
#include "revng/Clift/CliftTypes.cpp.inc"
using namespace clift;
using EmitErrorType = llvm::function_ref<mlir::InFlightDiagnostic()>;
//===----------------------- Implementation helpers -----------------------===//
static auto getEmitError(mlir::AsmParser &Parser, const mlir::SMLoc &Location) {
return [&Parser, Location]() { return Parser.emitError(Location); };
}
static auto getEmitError(mlir::DialectBytecodeReader &Reader) {
return [&Reader]() { return Reader.emitError(); };
}
static bool getDefinedTypeAliasImpl(DefinedType Type,
llvm::StringRef Kind,
llvm::raw_ostream &OS) {
if (not Type.getName().empty()) {
OS << Type.getName();
} else if (not Type.getHandle().empty()) {
OS << sanitizeIdentifier(Type.getHandle());
} else {
OS << Kind;
}
return true;
}
template<typename DefinedTypeT>
static bool getDefinedTypeAlias(DefinedTypeT Type, llvm::raw_ostream &OS) {
return getDefinedTypeAliasImpl(Type, DefinedTypeT::getMnemonic(), OS);
}
static void printString(mlir::AsmPrinter &Printer, llvm::StringRef String) {
Printer << '\"';
llvm::printEscapedString(String, Printer.getStream());
Printer << '\"';
}
static mlir::ParseResult parseSimpleStringAttributeImpl(mlir::AsmParser &Parser,
llvm::StringRef Name,
std::string &Value) {
if (Parser.parseOptionalKeyword(Name).succeeded()) {
if (Parser.parseString(&Value).failed())
return mlir::failure();
}
return mlir::success();
}
static void printSimpleStringAttributeImpl(mlir::AsmPrinter &Printer,
llvm::StringRef Name,
llvm::StringRef Value) {
if (not Value.empty()) {
Printer << " " << Name << " ";
printString(Printer, Value);
}
}
static mlir::ParseResult parseName(mlir::AsmParser &Parser,
std::string &Value) {
return parseSimpleStringAttributeImpl(Parser, "as", Value);
}
static void printName(mlir::AsmPrinter &Printer, llvm::StringRef Value) {
return printSimpleStringAttributeImpl(Printer, "as", Value);
}
static mlir::ParseResult parseComment(mlir::AsmParser &Parser,
std::string &Comment) {
return parseSimpleStringAttributeImpl(Parser, "comment", Comment);
}
static void printComment(mlir::AsmPrinter &Printer, llvm::StringRef Value) {
return printSimpleStringAttributeImpl(Printer, "comment", Value);
}
static mlir::LogicalResult readBool(bool &Value,
mlir::DialectBytecodeReader &Reader) {
uint64_t Integer;
if (Reader.readVarInt(Integer).failed())
return mlir::failure();
if (Integer > 1)
return mlir::failure();
Value = Integer != 0;
return mlir::success();
}
static void writeBool(bool Value, mlir::DialectBytecodeWriter &Writer) {
Writer.writeVarInt(Value);
}
//===------------------------------ LabelType -----------------------------===//
template<std::same_as<clift::LabelType>>
static clift::LabelType readType(mlir::DialectBytecodeReader &Reader) {
return clift::LabelType::get(Reader.getContext());
}
static void writeType(clift::LabelType Type,
mlir::DialectBytecodeWriter &Writer) {
}
//===------------------------------ VoidType ------------------------------===//
mlir::LogicalResult VoidType::verify(EmitErrorType EmitError, bool IsConst) {
return mlir::success();
}
bool VoidType::getAlias(llvm::raw_ostream &OS) const {
if (getIsConst())
return false;
OS << "void";
return true;
}
AddressableType VoidType::addConst() const {
if (isConst())
return *this;
return get(getContext(), /*IsConst=*/true);
}
AddressableType VoidType::removeConst() const {
if (not isConst())
return *this;
return get(getContext(), /*IsConst=*/false);
}
template<std::same_as<VoidType>>
static VoidType readType(mlir::DialectBytecodeReader &Reader) {
bool Const;
if (readBool(Const, Reader).failed())
return {};
return VoidType::get(Reader.getContext(), Const);
}
static void writeType(VoidType Type, mlir::DialectBytecodeWriter &Writer) {
writeBool(Type.getIsConst(), Writer);
}
//===----------------------------- IntegerType ----------------------------===//
mlir::LogicalResult IntegerType::verify(EmitErrorType EmitError,
IntegerKind Kind,
uint64_t Size,
bool IsConst) {
if (Size == 0)
return EmitError() << "Integer types must have non-zero size.";
return mlir::success();
}
static llvm::StringRef getIntegerKindAlias(IntegerKind Kind) {
switch (Kind) {
case IntegerKind::Signed:
return "int";
case IntegerKind::Unsigned:
return "uint";
default:
return stringifyIntegerKind(Kind);
}
}
bool IntegerType::getAlias(llvm::raw_ostream &OS) const {
if (getIsConst())
return false;
OS << getIntegerKindAlias(getKind()) << getSize() * 8 << "_t";
return true;
}
uint64_t IntegerType::getObjectSize() const {
return getSize();
}
AddressableType IntegerType::addConst() const {
if (isConst())
return *this;
return get(getContext(), getKind(), getSize(), /*IsConst=*/true);
}
AddressableType IntegerType::removeConst() const {
if (not isConst())
return *this;
return get(getContext(), getKind(), getSize(), /*IsConst=*/false);
}
template<std::same_as<IntegerType>>
static IntegerType readType(mlir::DialectBytecodeReader &Reader) {
uint64_t KindInteger;
if (Reader.readVarInt(KindInteger).failed())
return {};
auto Kind = symbolizeIntegerKind(KindInteger);
if (not Kind)
return {};
uint64_t Size;
if (Reader.readVarInt(Size).failed())
return {};
bool Const;
if (readBool(Const, Reader).failed())
return {};
return IntegerType::get(Reader.getContext(), *Kind, Size, Const);
}
static void writeType(IntegerType Type, mlir::DialectBytecodeWriter &Writer) {
Writer.writeVarInt(static_cast<uint64_t>(Type.getKind()));
Writer.writeVarInt(Type.getSize());
writeBool(Type.getIsConst(), Writer);
}
//===------------------------------ FloatType -----------------------------===//
mlir::LogicalResult
FloatType::verify(EmitErrorType EmitError, uint64_t Size, bool IsConst) {
if (Size == 0)
return EmitError() << "Floating point types must have non-zero size.";
return mlir::success();
}
bool FloatType::getAlias(llvm::raw_ostream &OS) const {
if (getIsConst())
return false;
OS << "float" << getSize() * 8 << "_t";
return true;
}
uint64_t FloatType::getObjectSize() const {
return getSize();
}
AddressableType FloatType::addConst() const {
if (isConst())
return *this;
return get(getContext(), getSize(), /*IsConst=*/true);
}
AddressableType FloatType::removeConst() const {
if (not isConst())
return *this;
return get(getContext(), getSize(), /*IsConst=*/false);
}
template<std::same_as<FloatType>>
static FloatType readType(mlir::DialectBytecodeReader &Reader) {
uint64_t Size;
if (Reader.readVarInt(Size).failed())
return {};
bool Const;
if (readBool(Const, Reader).failed())
return {};
return FloatType::get(Reader.getContext(), Size, Const);
}
static void writeType(FloatType Type, mlir::DialectBytecodeWriter &Writer) {
Writer.writeVarInt(Type.getSize());
writeBool(Type.getIsConst(), Writer);
}
//===----------------------------- PointerType ----------------------------===//
mlir::LogicalResult PointerType::verify(EmitErrorType EmitError,
mlir::Type ElementType,
uint64_t PointerSize,
bool IsConst) {
switch (PointerSize) {
case 4:
case 8:
break;
default:
return EmitError() << "invalid pointer size: " << PointerSize;
}
return mlir::success();
}
uint64_t PointerType::getObjectSize() const {
return getPointerSize();
}
AddressableType PointerType::addConst() const {
if (isConst())
return *this;
return get(getContext(),
getPointeeType(),
getPointerSize(),
/*IsConst=*/true);
}
AddressableType PointerType::removeConst() const {
if (not isConst())
return *this;
return get(getContext(),
getPointeeType(),
getPointerSize(),
/*IsConst=*/false);
}
template<std::same_as<clift::PointerType>>
static clift::PointerType readType(mlir::DialectBytecodeReader &Reader) {
mlir::Type PointeeType;
if (Reader.readType(PointeeType).failed())
return {};
uint64_t PointerSize;
if (Reader.readVarInt(PointerSize).failed())
return {};
bool Const;
if (readBool(Const, Reader).failed())
return {};
return clift::PointerType::get(PointeeType, PointerSize, Const);
}
static void writeType(clift::PointerType Type,
mlir::DialectBytecodeWriter &Writer) {
Writer.writeType(Type.getPointeeType());
Writer.writeVarInt(Type.getPointerSize());
writeBool(Type.getIsConst(), Writer);
}
//===------------------------------ ArrayType -----------------------------===//
mlir::LogicalResult ArrayType::verify(EmitErrorType EmitError,
mlir::Type ElementType,
uint64_t ElementCount) {
if (not clift::unwrapped_isa<ObjectType>(ElementType))
return EmitError() << "Array type element type must be an object type.";
if (ElementCount == 0)
return EmitError() << "Array type must have no less than one element.";
return mlir::success();
}
uint64_t ArrayType::getObjectSize() const {
return getElementsCount() * clift::getObjectSize(getElementType());
}
bool ArrayType::isConst() const {
return clift::isConst(getElementType());
}
AddressableType ArrayType::addConst() const {
auto ElementT = getElementType();
auto NewElementT = clift::addConst(ElementT);
if (ElementT == NewElementT)
return *this;
return get(getContext(), NewElementT, getElementsCount());
}
AddressableType ArrayType::removeConst() const {
auto ElementT = getElementType();
auto NewElementT = clift::removeConst(ElementT);
if (ElementT == NewElementT)
return *this;
return get(getContext(), NewElementT, getElementsCount());
}
template<std::same_as<clift::ArrayType>>
static clift::ArrayType readType(mlir::DialectBytecodeReader &Reader) {
mlir::Type ElementType;
if (Reader.readType(ElementType).failed())
return {};
uint64_t ElementCount;
if (Reader.readVarInt(ElementCount).failed())
return {};
return clift::ArrayType::get(ElementType, ElementCount);
}
static void writeType(clift::ArrayType Type,
mlir::DialectBytecodeWriter &Writer) {
Writer.writeType(Type.getElementType());
Writer.writeVarInt(Type.getElementsCount());
}
//===------------------------------ EnumType ------------------------------===//
bool EnumType::getAlias(llvm::raw_ostream &OS) const {
if (getIsConst())
return false;
return getDefinedTypeAlias(*this, OS);
}
uint64_t EnumType::getObjectSize() const {
return clift::getObjectSize(getUnderlyingType());
}
AddressableType EnumType::addConst() const {
return Base::get(getContext(), getDefinition(), /*IsConst=*/true);
}
AddressableType EnumType::removeConst() const {
return Base::get(getContext(), getDefinition(), /*IsConst=*/false);
}
bool EnumType::isSigned() const {
return clift::isSigned(getUnderlyingType());
}
bool EnumType::isUnsigned() const {
return clift::isUnsigned(getUnderlyingType());
}
mlir::Type EnumType::parse(mlir::AsmParser &Parser) {
mlir::SMLoc Loc = Parser.getCurrentLocation();
if (Parser.parseLess().failed())
return {};
std::string Handle;
if (Parser.parseString(&Handle).failed())
return {};
std::string Name;
if (parseName(Parser, Name).failed())
return {};
if (Parser.parseColon().failed())
return {};
mlir::Type UnderlyingType;
if (Parser.parseType(UnderlyingType).failed())
return {};
llvm::SmallVector<clift::EnumFieldAttr> Enumerators;
auto ParseEnumerator = [&]() -> mlir::ParseResult {
std::string Handle;
if (Parser.parseString(&Handle).failed())
return mlir::failure();
std::string Name;
if (parseName(Parser, Name).failed())
return mlir::failure();
if (Parser.parseColon().failed())
return mlir::failure();
uint64_t Value;
if (Parser.parseInteger(Value).failed())
return mlir::failure();
std::string Comment;
if (parseComment(Parser, Comment).failed())
return mlir::failure();
auto NameAttr = makeNameAttr<clift::EnumFieldAttr>(Parser.getContext(),
Handle,
Name);
auto CommentA = makeCommentAttr<clift::EnumFieldAttr>(Parser.getContext(),
Handle,
Comment);
auto Attr = clift::EnumFieldAttr::getChecked(getEmitError(Parser, Loc),
Parser.getContext(),
llvm::StringRef(Handle),
NameAttr,
CommentA,
Value);
if (not Attr)
return mlir::failure();
Enumerators.push_back(Attr);
return mlir::success();
};
if (Parser
.parseCommaSeparatedList(mlir::AsmParser::Delimiter::Braces,
ParseEnumerator,
" in enumerator list")
.failed())
return {};
std::string Comment;
if (parseComment(Parser, Comment).failed())
return {};
if (Parser.parseGreater().failed())
return {};
auto NameAttr = makeNameAttr<EnumAttr>(Parser.getContext(), Handle, Name);
auto CommentAttr = makeCommentAttr<EnumAttr>(Parser.getContext(),
Handle,
Comment);
auto Attr = EnumAttr::getChecked(getEmitError(Parser, Loc),
Parser.getContext(),
llvm::StringRef(Handle),
NameAttr,
CommentAttr,
UnderlyingType,
llvm::ArrayRef(Enumerators));
if (not Attr)
return {};
return EnumType::get(Attr);
}
void EnumType::print(mlir::AsmPrinter &Printer) const {
Printer << "<";
printString(Printer, getHandle());
printName(Printer, getName());
Printer << " : ";
Printer.printType(getUnderlyingType());
Printer << " {";
if (not getFields().empty()) {
for (auto [I, E] : llvm::enumerate(getFields())) {
if (I != 0)
Printer << ',';
Printer << "\n ";
printString(Printer, E.getHandle());
printName(Printer, E.getName());
Printer << " : " << E.getRawValue();
printComment(Printer, E.getComment());
}
Printer << '\n';
}
Printer << "}";
printComment(Printer, getComment());
Printer << ">";
}
template<std::same_as<clift::EnumType>>
static clift::EnumType readType(mlir::DialectBytecodeReader &Reader) {
clift::EnumAttr Definition;
if (Reader.readAttribute(Definition).failed())
return {};
bool Const;
if (readBool(Const, Reader).failed())
return {};
return clift::EnumType::get(Reader.getContext(), Definition, Const);
}
static void writeType(clift::EnumType Type,
mlir::DialectBytecodeWriter &Writer) {
Writer.writeAttribute(Type.getDefinition());
writeBool(Type.getIsConst(), Writer);
}
//===----------------------------- TypedefType ----------------------------===//
bool TypedefType::getAlias(llvm::raw_ostream &OS) const {
if (getIsConst())
return false;
return getDefinedTypeAlias(*this, OS);
}
AddressableType TypedefType::addConst() const {
return Base::get(getContext(), getDefinition(), /*IsConst=*/true);
}
AddressableType TypedefType::removeConst() const {
return Base::get(getContext(), getDefinition(), /*IsConst=*/false);
}
mlir::Type TypedefType::parse(mlir::AsmParser &Parser) {
mlir::SMLoc Loc = Parser.getCurrentLocation();
if (Parser.parseLess().failed())
return {};
std::string Handle;
if (Parser.parseString(&Handle).failed())
return {};
std::string Name;
if (parseName(Parser, Name).failed())
return {};
if (Parser.parseColon().failed())
return {};
mlir::Type UnderlyingType;
if (Parser.parseType(UnderlyingType).failed())
return {};
std::string Comment;
if (parseComment(Parser, Comment).failed())
return {};
if (Parser.parseGreater().failed())
return {};
auto NameAttr = makeNameAttr<TypedefAttr>(Parser.getContext(), Handle, Name);
auto CommentAttr = makeCommentAttr<TypedefAttr>(Parser.getContext(),
Handle,
Comment);
auto Attr = TypedefAttr::getChecked(getEmitError(Parser, Loc),
Parser.getContext(),
llvm::StringRef(Handle),
NameAttr,
CommentAttr,
UnderlyingType);
return TypedefType::get(Attr);
}
void TypedefType::print(mlir::AsmPrinter &Printer) const {
Printer << "<";
printString(Printer, getHandle());
printName(Printer, getName());
Printer << " : ";
Printer.printType(getUnderlyingType());
printComment(Printer, getComment());
Printer << ">";
}
template<std::same_as<clift::TypedefType>>
static clift::TypedefType readType(mlir::DialectBytecodeReader &Reader) {
clift::TypedefAttr Definition;
if (Reader.readAttribute(Definition).failed())
return {};
bool Const;
if (readBool(Const, Reader).failed())
return {};
return clift::TypedefType::get(Reader.getContext(), Definition, Const);
}
static void writeType(clift::TypedefType Type,
mlir::DialectBytecodeWriter &Writer) {
Writer.writeAttribute(Type.getDefinition());
writeBool(Type.getIsConst(), Writer);
}
//===---------------------------- FunctionType ----------------------------===//
mlir::LogicalResult
FunctionType::verify(EmitErrorType EmitError,
llvm::StringRef Handle,
MutableStringAttr Name,
MutableStringAttr Comment,
mlir::Type ReturnType,
llvm::ArrayRef<mlir::Type> Args,
llvm::ArrayRef<CAttributeAttr> Attributes) {
for (mlir::Type T : Args) {
if (not clift::unwrapped_isa<ValueType>(T))
return EmitError() << "Function parameter type must be a value type.";
}
if (not clift::unwrapped_isa<VoidType, ValueType>(ReturnType))
return EmitError() << "Function return type must be a value type or void.";
return mlir::success();
}
bool FunctionType::getAlias(llvm::raw_ostream &OS) const {
return getDefinedTypeAlias(*this, OS);
}
bool FunctionType::isConst() const {
return false;
}
AddressableType FunctionType::addConst() const {
return *this;
}
AddressableType FunctionType::removeConst() const {
return *this;
}
llvm::ArrayRef<mlir::Type> FunctionType::getResultTypes() const {
return llvm::ArrayRef<Type>(getImpl()->return_type);
}
static mlir::LogicalResult
parseAttributeArrayImpl(mlir::AsmParser &Parser,
llvm::SmallVector<clift::CAttributeAttr> &Out) {
revng_assert(Out.empty());
mlir::ArrayAttr RawAttributes;
auto MaybeResult = Parser.parseOptionalAttribute(RawAttributes);
if (not MaybeResult.has_value())
return mlir::success(); // no attributes - no problem
else if (MaybeResult.value().failed())
return mlir::failure();
for (mlir::Attribute RawAttribute : RawAttributes) {
if (auto CAttribute = mlir::cast<clift::CAttributeAttr>(RawAttribute))
Out.emplace_back(CAttribute);
else
return mlir::failure();
}
return mlir::success();
}
mlir::Type FunctionType::parse(mlir::AsmParser &Parser) {
mlir::SMLoc Loc = Parser.getCurrentLocation();
if (Parser.parseLess().failed())
return {};
std::string Handle;
if (Parser.parseString(&Handle).failed())
return {};
std::string Name;
if (parseName(Parser, Name).failed())
return {};
if (Parser.parseColon().failed())
return {};
mlir::Type ReturnType;
if (Parser.parseType(ReturnType).failed())
return {};
llvm::SmallVector<mlir::Type> ParameterTypes;
const auto ParseParameterType = [&Parser,
&ParameterTypes]() -> mlir::ParseResult {
mlir::Type ParameterType;
if (Parser.parseType(ParameterType))
return mlir::failure();
ParameterTypes.push_back(ParameterType);
return mlir::success();
};
if (Parser
.parseCommaSeparatedList(mlir::AsmParser::Delimiter::Paren,
ParseParameterType,
" in parameter list")
.failed())
return {};
llvm::SmallVector<clift::CAttributeAttr> Attributes;
if (parseAttributeArrayImpl(Parser, Attributes).failed())
return {};
std::string Comment;
if (parseComment(Parser, Comment).failed())
return {};
if (Parser.parseGreater().failed())
return {};
auto NameAttr = makeNameAttr<FunctionType>(Parser.getContext(), Handle, Name);
auto CommentAttr = makeCommentAttr<FunctionType>(Parser.getContext(),
Handle,
Comment);
return FunctionType::getChecked(getEmitError(Parser, Loc),
Parser.getContext(),
llvm::StringRef(Handle),
NameAttr,
CommentAttr,
ReturnType,
llvm::ArrayRef(ParameterTypes),
llvm::ArrayRef(Attributes));
}
void FunctionType::print(mlir::AsmPrinter &Printer) const {
Printer << "<";
printString(Printer, getHandle());
printName(Printer, getName());
Printer << " : ";
Printer.printType(getReturnType());
Printer << "(";
bool Comma = false;
for (mlir::Type ParameterType : getArgumentTypes()) {
if (Comma)
Printer << ", ";
Comma = true;
Printer.printType(ParameterType);
}
Printer << ")";
if (not getCAttributes().empty()) {
llvm::ArrayRef<mlir::Attribute> Attributes = { getCAttributes().begin(),
getCAttributes().end() };
Printer << '\n';
Printer.printAttribute(mlir::ArrayAttr::get(getContext(), Attributes));
}
printComment(Printer, getComment());
Printer << ">";
}
template<std::same_as<clift::FunctionType>>
static clift::FunctionType readType(mlir::DialectBytecodeReader &Reader) {
auto ReadType = [&](mlir::Type &Type) -> mlir::LogicalResult {
return Reader.readType(Type);
};
llvm::StringRef Handle;
if (Reader.readString(Handle).failed())
return {};
llvm::StringRef Name;
if (Reader.readString(Name).failed())
return {};
mlir::Type ReturnType;
if (ReadType(ReturnType).failed())
return {};
llvm::SmallVector<mlir::Type> ParameterTypes;
if (Reader.readList(ParameterTypes, ReadType).failed())
return {};
llvm::SmallVector<clift::CAttributeAttr> Attributes;
auto ReadAttribute = [&](clift::CAttributeAttr &Attribute) {
return Reader.readAttribute(Attribute);
};
if (Reader.readList(Attributes, ReadAttribute).failed())
return {};
llvm::StringRef Comment;
if (Reader.readString(Comment).failed())
return {};
mlir::MLIRContext *Context = Reader.getContext();
auto NameAttr = makeNameAttr<FunctionType>(Context, Handle, Name);
auto CommentAttr = makeCommentAttr<FunctionType>(Context, Handle, Comment);
return clift::FunctionType::getChecked(getEmitError(Reader),
Context,
Handle,
NameAttr,
CommentAttr,
ReturnType,
std::move(ParameterTypes),
llvm::ArrayRef(Attributes));
}
static void writeType(clift::FunctionType Type,
mlir::DialectBytecodeWriter &Writer) {
Writer.writeOwnedString(Type.getHandle());
Writer.writeOwnedString(Type.getName());
Writer.writeType(Type.getReturnType());
Writer.writeList(Type.getArgumentTypes(),
[&](mlir::Type Type) { Writer.writeType(Type); });
Writer.writeList(Type.getCAttributes(), [&](clift::CAttributeAttr Attribute) {
Writer.writeAttribute(Attribute);
});
Writer.writeOwnedString(Type.getComment());
}
//===----------------------------- Class types ----------------------------===//
// During parsing, contains (as an opaque pointer) the class type definition
// attribute (StructAttr or UnionAttr) of each class currently being parsed.
//
// During printing, contains (as an opaque pointer) each class type (StructType
// or UnionType) currently being printed.
static thread_local llvm::SmallPtrSet<const void *, 8> ClassRecursionSet;
template<typename TypeT, typename AttrT>
static TypeT parseClassType(mlir::AsmParser &Parser) {
constexpr bool IsStruct = std::is_same_v<TypeT, StructType>;
mlir::SMLoc Loc = Parser.getCurrentLocation();
if (Parser.parseLess().failed())
return {};
std::string Handle;
if (Parser.parseString(&Handle).failed())
return {};
auto IncompleteAttr = AttrT::get(Parser.getContext(), Handle);
const void *Opaque = IncompleteAttr.getAsOpaquePointer();
auto [Iterator, Inserted] = ClassRecursionSet.insert(Opaque);
auto Guard = llvm::make_scope_exit([&]() {
if (Inserted)
ClassRecursionSet.erase(Opaque);
});
if (Parser.parseOptionalGreater().succeeded())
return TypeT::get(Parser.getContext(), Handle);
std::string Name;
if (parseName(Parser, Name).failed())
return {};
if (Parser.parseColon().failed())
return {};
uint64_t Size;
if constexpr (IsStruct) {
if (Parser.parseKeyword("size").failed())
return {};
if (Parser.parseLParen().failed())
return {};
if (Parser.parseInteger(Size).failed())
return {};
if (Parser.parseRParen().failed())
return {};
}
llvm::SmallVector<FieldAttr> Fields;
auto ParseField = [&]() -> mlir::ParseResult {
auto FieldLoc = Parser.getCurrentLocation();
std::string Handle;
if (Parser.parseString(&Handle).failed())
return mlir::failure();
std::string Name;
if (parseName(Parser, Name))
return mlir::failure();
if (Parser.parseColon().failed())
return mlir::failure();
uint64_t Offset = 0;
if constexpr (IsStruct) {
if (Parser.parseKeyword("offset").failed())
return mlir::failure();
if (Parser.parseLParen().failed())
return mlir::failure();
if (Parser.parseInteger(Offset).failed())
return mlir::failure();
if (Parser.parseRParen().failed())
return mlir::failure();
}
mlir::Type Type;
if (Parser.parseType(Type).failed())
return mlir::failure();
std::string Comment;
if (parseComment(Parser, Comment).failed())
return {};
auto NameAttr = makeNameAttr<FieldAttr>(Parser.getContext(), Handle, Name);
auto CommentAttr = makeCommentAttr<FieldAttr>(Parser.getContext(),
Handle,
Comment);
auto Attr = FieldAttr::getChecked(getEmitError(Parser, FieldLoc),
Parser.getContext(),
llvm::StringRef(Handle),
NameAttr,
CommentAttr,
Offset,
Type);
if (not Attr)
return mlir::failure();
Fields.push_back(Attr);
return mlir::success();
};
if (Parser
.parseCommaSeparatedList(mlir::AsmParser::Delimiter::Braces,
ParseField,
" in field list")
.failed())
return {};
llvm::SmallVector<clift::CAttributeAttr> Attributes;
if (parseAttributeArrayImpl(Parser, Attributes).failed())
return {};
std::string Comment;
if (parseComment(Parser, Comment).failed())
return {};
if (Parser.parseGreater().failed())
return {};
auto GetCompleteType = [&](const auto &...Args) -> TypeT {
auto NameAttr = makeNameAttr<AttrT>(Parser.getContext(), Handle, Name);
auto CommentAttr = makeCommentAttr<AttrT>(Parser.getContext(),
Handle,
Comment);
auto Attr = AttrT::getChecked(getEmitError(Parser, Loc),
Parser.getContext(),
llvm::StringRef(Handle),
NameAttr,
CommentAttr,
Args...,
llvm::ArrayRef(Fields),
llvm::ArrayRef(Attributes));
if (not Attr)
return {};
return TypeT::get(Attr);
};
if constexpr (IsStruct) {
return GetCompleteType(Size);
} else {
return GetCompleteType();
}
}
template<typename TypeT>
static void printClassType(TypeT Type, mlir::AsmPrinter &Printer) {
constexpr bool IsStruct = std::is_same_v<TypeT, StructType>;
auto PrintIncomplete = [&]() {
Printer << '<';
printString(Printer, Type.getHandle());
Printer << '>';
};
const void *Opaque = Type.getAsOpaquePointer();
const void *CurrentAlias = Printer.getCurrentTypeOrAttrAlias();
// When printing an alias (CurrentAlias != nullptr), the full definition is
// only printed if that alias is for this exact type (CurrentAlias == Opaque).
if ((CurrentAlias != nullptr and CurrentAlias != Opaque))
return PrintIncomplete();
// Whether printing an alias or not, the full definition is never printed
// recursively within the definition of the same type.
if (not ClassRecursionSet.insert(Opaque).second)
return PrintIncomplete();
auto Guard = llvm::make_scope_exit([&]() {
ClassRecursionSet.erase(Opaque);
});
Printer << "<";
printString(Printer, Type.getHandle());
printName(Printer, Type.getName());
Printer << " : ";
if constexpr (IsStruct) {
Printer << "size(" << Type.getSize() << ") ";
}
Printer << "{";
if (not Type.getFields().empty()) {
for (auto [I, S] : llvm::enumerate(Type.getFields())) {
if (I != 0)
Printer << ',';
Printer << "\n ";
printString(Printer, S.getHandle());
printName(Printer, S.getName());
Printer << " :";
if constexpr (IsStruct) {
Printer << " offset(" << S.getOffset() << ")";
}
Printer << ' ' << S.getType();
printComment(Printer, S.getComment());
}
Printer << '\n';
}
Printer << "}";
if (not Type.getDefinition().getCAttributes().empty()) {
llvm::SmallVector<mlir::Attribute> Attributes;
for (mlir::Attribute Attribute : Type.getDefinition().getCAttributes())
Attributes.emplace_back(Attribute);
Printer << '\n';
Printer.printAttribute(mlir::ArrayAttr::get(Type.getContext(), Attributes));
}
printComment(Printer, Type.getComment());
Printer << ">";
}
template<typename AttrT>
static uint64_t getClassTypeSize(AttrT Definition) {
if (not Definition.hasDefinition()) {
if (ClassRecursionSet.contains(Definition.getAsOpaquePointer())) {
// We can return zero for any struct still being parsed. This avoids
// assert failure when verifying recursively defined structs. Recursively
// defined class types are rejected later during module verification.
return 0;
}
}
return Definition.getSize();
}
static thread_local llvm::SmallPtrSet<const void *, 32> BytecodeClassTypeSet;
template<typename TypeT, typename AttrT>
static TypeT readClassType(mlir::DialectBytecodeReader &Reader) {
constexpr bool IsStruct = std::is_same_v<TypeT, StructType>;
llvm::StringRef Handle;
if (Reader.readString(Handle).failed())
return {};
bool Const;
if (readBool(Const, Reader).failed())
return {};
auto Type = TypeT::get(Reader.getContext(), Handle);
const void *Opaque = Type.getAsOpaquePointer();
auto [Iterator, Inserted] = BytecodeClassTypeSet.insert(Opaque);
auto Guard = llvm::make_scope_exit([&]() {
if (Inserted)
BytecodeClassTypeSet.erase(Opaque);
});
// This is the complicated part of class type serialization.
//
// First, a little bit of background:
//
// The bytecode serializer first visits each type recursively, recording all
// reachable types and assigning them numeric ids. After that it visits each
// type again non-recursively, serializing them into a table, with references
// to other types serialized using their numeric ids. The same applies to
// attributes
//
// The bytecode deserializer deserializes types and attributes from the table
// on demand. When a type or attribute is fully deserialized for the first
// time, it is remembered and not deserialized again.
//
// Now the problem with recursive types:
//
// When deserializing a recursive class, using the normal rules, it is never
// *fully* deserialized, because the same type must be deserialized again and
// again forever. To get around this we employ a small hack:
//
// 1. Instead of storing the type in a single blob as usual, we store the
// handle and constness of type, as well as a BytecodeClassAttr attribute
// referencing the type itself. This creates an indirection in the table
// as explained above. The definition of the class type is deserialized
// as part of the attribute deserialization.
// See `readClassDefinition` and `writeClassDefinition` below.
//
// 2. When recursion is detected during deserialization, instead of
// deserializing the contained attribute (which is resolved through the
// table) again, we read the underlying integer and simply skip it, ending
// the recursion. At this point the class type has already been defined.
if (Inserted) {
clift::BytecodeClassAttr HelperAttr;
if (Reader.readAttribute(HelperAttr).failed())
return {};
revng_assert(HelperAttr.getType() == Type);
revng_assert(Type.isComplete());
} else {
// Here we skip the deserialization of the helper attribute by reading and
// ignoring the underlying table reference:
uint64_t HelperAttrReference;
if (Reader.readVarInt(HelperAttrReference).failed())
return {};
}
if (Const)
Type = mlir::cast<TypeT>(Type.addConst());
return Type;
}
template<typename TypeT>
static void writeClassType(TypeT Type, mlir::DialectBytecodeWriter &Writer) {
constexpr bool IsStruct = std::is_same_v<TypeT, StructType>;
Writer.writeOwnedString(Type.getHandle());
writeBool(Type.getIsConst(), Writer);
Writer.writeAttribute(BytecodeClassAttr::get(Type.getContext(), Type));
}
template<typename TypeT, typename AttrT>
static TypeT readClassDefinition(mlir::DialectBytecodeReader &Reader) {
constexpr bool IsStruct = std::is_same_v<TypeT, StructType>;
llvm::StringRef Handle;
if (Reader.readString(Handle).failed())
return {};
llvm::StringRef Name;
if (Reader.readString(Name).failed())
return {};
uint64_t Size = 0;
if constexpr (IsStruct) {
if (Reader.readVarInt(Size).failed())
return {};
}
auto ReadField = [&](clift::FieldAttr &Field) -> mlir::LogicalResult {
llvm::StringRef Handle;
if (Reader.readString(Handle).failed())
return mlir::failure();
llvm::StringRef Name;
if (Reader.readString(Name).failed())
return mlir::failure();
uint64_t Offset = 0;
if constexpr (IsStruct) {
if (Reader.readVarInt(Offset).failed())
return mlir::failure();
}
mlir::Type Type;
if (Reader.readType(Type).failed())
return mlir::failure();
llvm::StringRef Comment;
if (Reader.readString(Comment).failed())
return mlir::failure();
auto NameAttr = makeNameAttr<clift::FieldAttr>(Reader.getContext(),
Handle,
Name);
auto CommentAttr = makeCommentAttr<clift::FieldAttr>(Reader.getContext(),
Handle,
Comment);
Field = clift::FieldAttr::getChecked(getEmitError(Reader),
Reader.getContext(),
llvm::StringRef(Handle),
NameAttr,
CommentAttr,
Offset,
Type);
return mlir::success(static_cast<bool>(Field));
};
llvm::SmallVector<clift::FieldAttr> Fields;
if (Reader.readList(Fields, ReadField).failed())
return {};
llvm::SmallVector<clift::CAttributeAttr> Attributes;
auto ReadAttribute = [&](clift::CAttributeAttr &Attribute) {
return Reader.readAttribute(Attribute);
};
if (Reader.readList(Attributes, ReadAttribute).failed())
return {};
llvm::StringRef Comment;
if (Reader.readString(Comment).failed())
return {};
auto GetCompleteType = [&](const auto &...Args) -> TypeT {
auto NameAttr = makeNameAttr<AttrT>(Reader.getContext(), Handle, Name);
auto CommentAttr = makeCommentAttr<AttrT>(Reader.getContext(),
Handle,
Comment);
auto Attr = AttrT::getChecked(getEmitError(Reader),
Reader.getContext(),
llvm::StringRef(Handle),
NameAttr,
CommentAttr,
Args...,
llvm::ArrayRef(Fields),
Attributes);
if (not Attr)
return {};
return TypeT::get(Attr);
};
if constexpr (IsStruct) {
return GetCompleteType(Size);
} else {
return GetCompleteType();
}
}
template<typename TypeT>
static void
writeClassDefinition(TypeT Type, mlir::DialectBytecodeWriter &Writer) {
constexpr bool IsStruct = std::is_same_v<TypeT, StructType>;
Writer.writeOwnedString(Type.getHandle());
Writer.writeOwnedString(Type.getName());
if constexpr (IsStruct) {
Writer.writeVarInt(Type.getSize());
}
Writer.writeList(Type.getFields(), [&](clift::FieldAttr Field) {
Writer.writeOwnedString(Field.getHandle());
Writer.writeOwnedString(Field.getName());
if constexpr (IsStruct) {
Writer.writeVarInt(Field.getOffset());
}
Writer.writeType(Field.getType());
Writer.writeOwnedString(Field.getComment());
});
Writer.writeList(Type.getCAttributes(), [&](clift::CAttributeAttr Attribute) {
Writer.writeAttribute(Attribute);
});
Writer.writeOwnedString(Type.getComment());
}
//===----------------------------- StructType -----------------------------===//
uint64_t StructType::getSize() const {
return getClassTypeSize(getDefinition());
}
bool StructType::getAlias(llvm::raw_ostream &OS) const {
if (getIsConst())
return false;
return getDefinedTypeAlias(*this, OS);
}
mlir::Type StructType::parse(mlir::AsmParser &Parser) {
return parseClassType<StructType, StructAttr>(Parser);
}
void StructType::print(mlir::AsmPrinter &Printer) const {
return printClassType(*this, Printer);
}
template<std::same_as<clift::StructType>>
static clift::StructType readType(mlir::DialectBytecodeReader &Reader) {
return readClassType<StructType, StructAttr>(Reader);
}
static void writeType(clift::StructType Type,
mlir::DialectBytecodeWriter &Writer) {
return writeClassType<StructType>(Type, Writer);
}
clift::StructType
clift::readStructDefinition(mlir::DialectBytecodeReader &Reader) {
return readClassDefinition<StructType, StructAttr>(Reader);
}
void clift::writeStructDefinition(clift::StructType Type,
mlir::DialectBytecodeWriter &Writer) {
return writeClassDefinition(Type, Writer);
}
//===------------------------------ UnionType -----------------------------===//
uint64_t UnionType::getSize() const {
return getClassTypeSize(getDefinition());
}
bool UnionType::getAlias(llvm::raw_ostream &OS) const {
if (getIsConst())
return false;
return getDefinedTypeAlias(*this, OS);
}
mlir::Type UnionType::parse(mlir::AsmParser &Parser) {
return parseClassType<UnionType, UnionAttr>(Parser);
}
void UnionType::print(mlir::AsmPrinter &Printer) const {
return printClassType(*this, Printer);
}
template<std::same_as<clift::UnionType>>
static clift::UnionType readType(mlir::DialectBytecodeReader &Reader) {
return readClassType<UnionType, UnionAttr>(Reader);
}
static void writeType(clift::UnionType Type,
mlir::DialectBytecodeWriter &Writer) {
return writeClassType<UnionType>(Type, Writer);
}
clift::UnionType
clift::readUnionDefinition(mlir::DialectBytecodeReader &Reader) {
return readClassDefinition<UnionType, UnionAttr>(Reader);
}
void clift::writeUnionDefinition(clift::UnionType Type,
mlir::DialectBytecodeWriter &Writer) {
return writeClassDefinition(Type, Writer);
}
//===---------------------------- Type helpers ----------------------------===//
//===------------------------------ Typedefs ------------------------------===//
TypedefDecomposition clift::decomposeTypedef(mlir::Type Type) {
bool HasConstTypedef = false;
while (true) {
auto T = mlir::dyn_cast<TypedefType>(Type);
if (not T)
break;
Type = T.getUnderlyingType();
HasConstTypedef |= T.getIsConst();
}
return { Type, HasConstTypedef };
}
mlir::Type clift::unwrapTypedefs(mlir::Type Type) {
return decomposeTypedef(Type).Type;
}
mlir::Type clift::collapseTypedefs(mlir::Type Type) {
auto [UnderlyingType, HasConstTypedef] = decomposeTypedef(Type);
if (HasConstTypedef)
UnderlyingType = addConst(UnderlyingType);
return UnderlyingType;
}
//===---------------------------- CV-Qualifiers ---------------------------===//
bool clift::isConst(mlir::Type Type) {
if (auto ValueT = mlir::dyn_cast<AddressableType>(Type))
return ValueT.isConst();
return false;
}
mlir::Type clift::addConst(mlir::Type Type) {
if (auto ValueT = mlir::dyn_cast<AddressableType>(Type))
Type = ValueT.addConst();
return Type;
}
mlir::Type clift::removeConst(mlir::Type Type) {
if (auto ValueT = mlir::dyn_cast<AddressableType>(Type))
Type = ValueT.removeConst();
return Type;
}
bool clift::equivalent(mlir::Type Lhs, mlir::Type Rhs) {
if (Lhs == Rhs)
return true;
if (not Lhs or not Rhs)
return false;
auto LhsVT = mlir::dyn_cast<AddressableType>(Lhs);
if (not LhsVT)
return false;
auto RhsVT = mlir::dyn_cast<AddressableType>(Rhs);
if (not RhsVT)
return false;
return removeConst(LhsVT) == removeConst(RhsVT);
}
//===-------------------------- Object type size --------------------------===//
uint64_t clift::getObjectSize(mlir::Type Type) {
return clift::unwrapped_cast<ObjectType>(Type).getObjectSize();
}
uint64_t clift::getObjectSizeOrZero(mlir::Type Type) {
if (auto ValueT = clift::unwrapped_dyn_cast<ObjectType>(Type))
return ValueT.getObjectSize();
return 0;
}
//===--------------------------- Integral types ---------------------------===//
bool clift::isSigned(mlir::Type Type) {
auto T = clift::unwrapped_dyn_cast<IntegralType>(Type);
return T and T.isSigned();
}
bool clift::isUnsigned(mlir::Type Type) {
auto T = clift::unwrapped_dyn_cast<IntegralType>(Type);
return T and T.isUnsigned();
}
//===--------------------------- Type categories --------------------------===//
bool clift::isModifiableType(mlir::Type Type) {
auto [UnderlyingType, HasConst] = decomposeTypedef(Type);
return not HasConst and not isConst(UnderlyingType);
}
IntegerType clift::getUnderlyingIntegerType(mlir::Type Type) {
Type = unwrapTypedefs(Type);
if (auto T = mlir::dyn_cast<IntegerType>(Type))
return T;
if (auto T = mlir::dyn_cast<EnumType>(Type))
return mlir::cast<IntegerType>(collapseTypedefs(T.getUnderlyingType()));
return nullptr;
}
bool clift::isCompleteType(mlir::Type Type) {
Type = unwrapTypedefs(Type);
while (auto Array = mlir::dyn_cast<ArrayType>(Type))
Type = unwrapTypedefs(Array.getElementType());
if (auto Class = mlir::dyn_cast<ClassType>(Type))
return Class.isComplete();
return true;
}
bool clift::isScalarType(mlir::Type Type) {
return clift::unwrapped_isa<IntegerType, //
FloatType,
EnumType,
PointerType>(Type);
}
bool clift::isBooleanType(mlir::Type Type) {
auto T = clift::unwrapped_dyn_cast<IntegerType>(Type);
return T and T.isSigned();
}
clift::FunctionType
clift::getFunctionOrFunctionPointerFunctionType(mlir::Type Type) {
Type = unwrapTypedefs(Type);
if (auto P = mlir::dyn_cast<PointerType>(Type))
Type = unwrapTypedefs(P.getPointeeType());
return mlir::dyn_cast<clift::FunctionType>(Type);
}
//===---------------------------- CliftDialect ----------------------------===//
void CliftDialect::registerTypes() {
addTypes</* Include the auto-generated clift types */
#define GET_TYPEDEF_LIST
#include "revng/Clift/CliftTypes.cpp.inc"
/* End of auto-generated list */>();
}
static AddressableType parseConstType(mlir::DialectAsmParser &Parser) {
if (Parser.parseLess().failed())
return {};
AddressableType UnderlyingType;
if (Parser.parseType(UnderlyingType).failed())
return {};
if (Parser.parseGreater().failed())
return {};
return UnderlyingType.addConst();
}
static void printConstType(AddressableType Type,
mlir::DialectAsmPrinter &Printer) {
Printer << "const<";
Printer.printType(Type);
Printer << ">";
}
/// Parse a type registered to this dialect
mlir::Type CliftDialect::parseType(mlir::DialectAsmParser &Parser) const {
const llvm::SMLoc TypeLoc = Parser.getCurrentLocation();
llvm::StringRef Mnemonic;
if (mlir::Type T; generatedTypeParser(Parser, &Mnemonic, T).has_value())
return T;
if (Mnemonic == "const")
return parseConstType(Parser);
Parser.emitError(TypeLoc) << "unknown type `" << Mnemonic << "` in dialect `"
<< getNamespace() << "`";
return {};
}
/// Print a type registered to this dialect
void CliftDialect::printType(mlir::Type Type,
mlir::DialectAsmPrinter &Printer) const {
if (auto ConstType = mlir::dyn_cast<AddressableType>(Type)) {
if (ConstType.isConst())
return printConstType(ConstType.removeConst(), Printer);
}
if (mlir::succeeded(generatedTypePrinter(Type, Printer)))
return;
revng_abort("cannot print type");
}
namespace {
enum class CliftTypeKind : uint8_t {
Label,
Void,
Int,
Float,
Pointer,
Array,
Enum,
Typedef,
Function,
Struct,
Union,
N
};
} // namespace
static mlir::LogicalResult readTypeKind(CliftTypeKind &TypeKind,
mlir::DialectBytecodeReader &Reader) {
uint64_t Value;
if (Reader.readVarInt(Value).failed())
return mlir::failure();
if (Value >= static_cast<uint64_t>(CliftTypeKind::N))
return mlir::failure();
TypeKind = static_cast<CliftTypeKind>(Value);
return mlir::success();
}
mlir::Type clift::readType(mlir::DialectBytecodeReader &Reader) {
CliftTypeKind TypeKind;
if (readTypeKind(TypeKind, Reader).failed())
return {};
switch (TypeKind) {
case CliftTypeKind::Label:
return ::readType<clift::LabelType>(Reader);
case CliftTypeKind::Void:
return ::readType<clift::VoidType>(Reader);
case CliftTypeKind::Int:
return ::readType<clift::IntegerType>(Reader);
case CliftTypeKind::Float:
return ::readType<clift::FloatType>(Reader);
case CliftTypeKind::Pointer:
return ::readType<clift::PointerType>(Reader);
case CliftTypeKind::Array:
return ::readType<clift::ArrayType>(Reader);
case CliftTypeKind::Enum:
return ::readType<clift::EnumType>(Reader);
case CliftTypeKind::Typedef:
return ::readType<clift::TypedefType>(Reader);
case CliftTypeKind::Function:
return ::readType<clift::FunctionType>(Reader);
case CliftTypeKind::Struct:
return ::readType<clift::StructType>(Reader);
case CliftTypeKind::Union:
return ::readType<clift::UnionType>(Reader);
case CliftTypeKind::N:
break;
}
revng_abort();
}
mlir::LogicalResult clift::writeType(mlir::Type Type,
mlir::DialectBytecodeWriter &Writer) {
auto Write = [&](auto T, CliftTypeKind TypeKind) {
Writer.writeVarInt(static_cast<uint64_t>(TypeKind));
::writeType(T, Writer);
return mlir::success();
};
if (auto T = mlir::dyn_cast<clift::LabelType>(Type))
return Write(T, CliftTypeKind::Label);
if (auto T = mlir::dyn_cast<clift::VoidType>(Type))
return Write(T, CliftTypeKind::Void);
if (auto T = mlir::dyn_cast<clift::IntegerType>(Type))
return Write(T, CliftTypeKind::Int);
if (auto T = mlir::dyn_cast<clift::FloatType>(Type))
return Write(T, CliftTypeKind::Float);
if (auto T = mlir::dyn_cast<clift::PointerType>(Type))
return Write(T, CliftTypeKind::Pointer);
if (auto T = mlir::dyn_cast<clift::ArrayType>(Type))
return Write(T, CliftTypeKind::Array);
if (auto T = mlir::dyn_cast<clift::EnumType>(Type))
return Write(T, CliftTypeKind::Enum);
if (auto T = mlir::dyn_cast<clift::TypedefType>(Type))
return Write(T, CliftTypeKind::Typedef);
if (auto T = mlir::dyn_cast<clift::FunctionType>(Type))
return Write(T, CliftTypeKind::Function);
if (auto T = mlir::dyn_cast<clift::StructType>(Type))
return Write(T, CliftTypeKind::Struct);
if (auto T = mlir::dyn_cast<clift::UnionType>(Type))
return Write(T, CliftTypeKind::Union);
return mlir::failure();
}