// // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #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; //===----------------------- 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 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> 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> 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> 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(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> 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> 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(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> 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 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(Parser.getContext(), Handle, Name); auto CommentA = makeCommentAttr(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(Parser.getContext(), Handle, Name); auto CommentAttr = makeCommentAttr(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> 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(Parser.getContext(), Handle, Name); auto CommentAttr = makeCommentAttr(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> 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 Args, llvm::ArrayRef Attributes) { for (mlir::Type T : Args) { if (not clift::unwrapped_isa(T)) return EmitError() << "Function parameter type must be a value type."; } if (not clift::unwrapped_isa(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 FunctionType::getResultTypes() const { return llvm::ArrayRef(getImpl()->return_type); } static mlir::LogicalResult parseAttributeArrayImpl(mlir::AsmParser &Parser, llvm::SmallVector &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(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 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 Attributes; if (parseAttributeArrayImpl(Parser, Attributes).failed()) return {}; std::string Comment; if (parseComment(Parser, Comment).failed()) return {}; if (Parser.parseGreater().failed()) return {}; auto NameAttr = makeNameAttr(Parser.getContext(), Handle, Name); auto CommentAttr = makeCommentAttr(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 Attributes = { getCAttributes().begin(), getCAttributes().end() }; Printer << '\n'; Printer.printAttribute(mlir::ArrayAttr::get(getContext(), Attributes)); } printComment(Printer, getComment()); Printer << ">"; } template> 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 ParameterTypes; if (Reader.readList(ParameterTypes, ReadType).failed()) return {}; llvm::SmallVector 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(Context, Handle, Name); auto CommentAttr = makeCommentAttr(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 ClassRecursionSet; template static TypeT parseClassType(mlir::AsmParser &Parser) { constexpr bool IsStruct = std::is_same_v; 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 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(Parser.getContext(), Handle, Name); auto CommentAttr = makeCommentAttr(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 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(Parser.getContext(), Handle, Name); auto CommentAttr = makeCommentAttr(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 static void printClassType(TypeT Type, mlir::AsmPrinter &Printer) { constexpr bool IsStruct = std::is_same_v; 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 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 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 BytecodeClassTypeSet; template static TypeT readClassType(mlir::DialectBytecodeReader &Reader) { constexpr bool IsStruct = std::is_same_v; 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(Type.addConst()); return Type; } template static void writeClassType(TypeT Type, mlir::DialectBytecodeWriter &Writer) { constexpr bool IsStruct = std::is_same_v; Writer.writeOwnedString(Type.getHandle()); writeBool(Type.getIsConst(), Writer); Writer.writeAttribute(BytecodeClassAttr::get(Type.getContext(), Type)); } template static TypeT readClassDefinition(mlir::DialectBytecodeReader &Reader) { constexpr bool IsStruct = std::is_same_v; 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(Reader.getContext(), Handle, Name); auto CommentAttr = makeCommentAttr(Reader.getContext(), Handle, Comment); Field = clift::FieldAttr::getChecked(getEmitError(Reader), Reader.getContext(), llvm::StringRef(Handle), NameAttr, CommentAttr, Offset, Type); return mlir::success(static_cast(Field)); }; llvm::SmallVector Fields; if (Reader.readList(Fields, ReadField).failed()) return {}; llvm::SmallVector 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(Reader.getContext(), Handle, Name); auto CommentAttr = makeCommentAttr(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 static void writeClassDefinition(TypeT Type, mlir::DialectBytecodeWriter &Writer) { constexpr bool IsStruct = std::is_same_v; 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(Parser); } void StructType::print(mlir::AsmPrinter &Printer) const { return printClassType(*this, Printer); } template> static clift::StructType readType(mlir::DialectBytecodeReader &Reader) { return readClassType(Reader); } static void writeType(clift::StructType Type, mlir::DialectBytecodeWriter &Writer) { return writeClassType(Type, Writer); } clift::StructType clift::readStructDefinition(mlir::DialectBytecodeReader &Reader) { return readClassDefinition(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(Parser); } void UnionType::print(mlir::AsmPrinter &Printer) const { return printClassType(*this, Printer); } template> static clift::UnionType readType(mlir::DialectBytecodeReader &Reader) { return readClassType(Reader); } static void writeType(clift::UnionType Type, mlir::DialectBytecodeWriter &Writer) { return writeClassType(Type, Writer); } clift::UnionType clift::readUnionDefinition(mlir::DialectBytecodeReader &Reader) { return readClassDefinition(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(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(Type)) return ValueT.isConst(); return false; } mlir::Type clift::addConst(mlir::Type Type) { if (auto ValueT = mlir::dyn_cast(Type)) Type = ValueT.addConst(); return Type; } mlir::Type clift::removeConst(mlir::Type Type) { if (auto ValueT = mlir::dyn_cast(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(Lhs); if (not LhsVT) return false; auto RhsVT = mlir::dyn_cast(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(Type).getObjectSize(); } uint64_t clift::getObjectSizeOrZero(mlir::Type Type) { if (auto ValueT = clift::unwrapped_dyn_cast(Type)) return ValueT.getObjectSize(); return 0; } //===--------------------------- Integral types ---------------------------===// bool clift::isSigned(mlir::Type Type) { auto T = clift::unwrapped_dyn_cast(Type); return T and T.isSigned(); } bool clift::isUnsigned(mlir::Type Type) { auto T = clift::unwrapped_dyn_cast(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(Type)) return T; if (auto T = mlir::dyn_cast(Type)) return mlir::cast(collapseTypedefs(T.getUnderlyingType())); return nullptr; } bool clift::isCompleteType(mlir::Type Type) { Type = unwrapTypedefs(Type); while (auto Array = mlir::dyn_cast(Type)) Type = unwrapTypedefs(Array.getElementType()); if (auto Class = mlir::dyn_cast(Type)) return Class.isComplete(); return true; } bool clift::isScalarType(mlir::Type Type) { return clift::unwrapped_isa(Type); } bool clift::isBooleanType(mlir::Type Type) { auto T = clift::unwrapped_dyn_cast(Type); return T and T.isSigned(); } clift::FunctionType clift::getFunctionOrFunctionPointerFunctionType(mlir::Type Type) { Type = unwrapTypedefs(Type); if (auto P = mlir::dyn_cast(Type)) Type = unwrapTypedefs(P.getPointeeType()); return mlir::dyn_cast(Type); } //===---------------------------- CliftDialect ----------------------------===// void CliftDialect::registerTypes() { addTypes(); } 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(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(CliftTypeKind::N)) return mlir::failure(); TypeKind = static_cast(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(Reader); case CliftTypeKind::Void: return ::readType(Reader); case CliftTypeKind::Int: return ::readType(Reader); case CliftTypeKind::Float: return ::readType(Reader); case CliftTypeKind::Pointer: return ::readType(Reader); case CliftTypeKind::Array: return ::readType(Reader); case CliftTypeKind::Enum: return ::readType(Reader); case CliftTypeKind::Typedef: return ::readType(Reader); case CliftTypeKind::Function: return ::readType(Reader); case CliftTypeKind::Struct: return ::readType(Reader); case CliftTypeKind::Union: return ::readType(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(TypeKind)); ::writeType(T, Writer); return mlir::success(); }; if (auto T = mlir::dyn_cast(Type)) return Write(T, CliftTypeKind::Label); if (auto T = mlir::dyn_cast(Type)) return Write(T, CliftTypeKind::Void); if (auto T = mlir::dyn_cast(Type)) return Write(T, CliftTypeKind::Int); if (auto T = mlir::dyn_cast(Type)) return Write(T, CliftTypeKind::Float); if (auto T = mlir::dyn_cast(Type)) return Write(T, CliftTypeKind::Pointer); if (auto T = mlir::dyn_cast(Type)) return Write(T, CliftTypeKind::Array); if (auto T = mlir::dyn_cast(Type)) return Write(T, CliftTypeKind::Enum); if (auto T = mlir::dyn_cast(Type)) return Write(T, CliftTypeKind::Typedef); if (auto T = mlir::dyn_cast(Type)) return Write(T, CliftTypeKind::Function); if (auto T = mlir::dyn_cast(Type)) return Write(T, CliftTypeKind::Struct); if (auto T = mlir::dyn_cast(Type)) return Write(T, CliftTypeKind::Union); return mlir::failure(); }