// // This file is distributed under the MIT License. See LICENSE.md for details. // #include "llvm/ADT/PostOrderIterator.h" #include "revng/ABI/FunctionType/Layout.h" #include "revng/PTML/CAttributes.h" #include "revng/TypeNames/ModelCBuilder.h" using T = model::TypeDefinition; void ptml::ModelCBuilder::printForwardDeclaration(const T &Type) { revng_assert(not isDeclarationTheSameAsDefinition(Type)); auto TypeNameReference = getReferenceTag(Type); *Out << getKeyword(ptml::CBuilder::Keyword::Typedef) << " " << getTypeKeyword(Type) << " " << ptml::Attributes.getAttributeString<"_PACKED">() << " " << TypeNameReference << " " << TypeNameReference << ";\n"; } void ptml::ModelCBuilder::printDefinition(const model::EnumDefinition &E, std::string &&Suffix) { // We have to make the enum of the correct size of the underlying type auto ByteSize = *E.size(); revng_assert(ByteSize <= 8); size_t FullMask = std::numeric_limits::max(); size_t MaxBitPatternInEnum = (ByteSize == 8) ? FullMask : ((FullMask) xor (FullMask << (8 * ByteSize))); std::string UndTag = getReferenceTag(E.underlyingType()); auto U = ptml::Attributes.getAnnotationString<"_ENUM_UNDERLYING">(UndTag); std::string EnumLine = getModelCommentWithoutLeadingNewline(E) + getKeyword(ptml::CBuilder::Keyword::Enum) + " " + U + " " + ptml::Attributes.getAttributeString<"_PACKED">() + " " + getDefinitionTag(E) + " "; *Out << getCommentableTag(std::move(EnumLine), E); { Scope Scope(*Out, ptml::c::scopes::EnumBody); using COperator = ptml::CBuilder::Operator; for (const auto &Entry : E.Entries()) { std::string Result = getModelComment(Entry) + getDefinitionTag(E, Entry) + " " + getOperator(COperator::Assign) + " " + getHex(Entry.Value()) + ','; *Out << getCommentableTag(std::move(Result), E, Entry) << '\n'; } if (Configuration.EnablePrintingOfTheMaximumEnumValue) { // This ensures the enum is exactly as large as the Underlying type const auto &Prefix = NameBuilder.Configuration.MaximumEnumValuePrefix(); *Out << tokenTag(Prefix + NameBuilder.name(E), ptml::c::tokens::Field) << " " + getOperator(COperator::Assign) + " " << getHex(MaxBitPatternInEnum) << ",\n"; } } *Out << std::move(Suffix) << ";\n"; } void ptml::ModelCBuilder::printPadding(uint64_t FieldOffset, uint64_t NextOffset) { if (Configuration.EnableExplicitPadding) { revng_assert(FieldOffset <= NextOffset); if (FieldOffset == NextOffset) return; // There is no padding *Out << tokenTag("uint8_t", ptml::c::tokens::Type) << " " << tokenTag(NameBuilder.paddingFieldName(FieldOffset), ptml::c::tokens::Field) << "[" << getNumber(NextOffset - FieldOffset) << "];\n"; } } void ptml::ModelCBuilder::printDefinition(const model::StructDefinition &S, std::string &&Suffix) { std::string StructLine = getModelCommentWithoutLeadingNewline(S) + getKeyword(ptml::CBuilder::Keyword::Struct) + " " + ptml::Attributes.getAttributeString<"_PACKED">() + " "; if (S.CanContainCode()) StructLine += ptml::Attributes.getAttributeString<"_CAN_CONTAIN_CODE">() + " "; StructLine += ptml::Attributes.getAnnotationString<"_SIZE">(S.Size()) + " " + getDefinitionTag(S) + " "; *Out << getCommentableTag(std::move(StructLine), S); { Scope Scope(*Out, ptml::c::scopes::StructBody); size_t PreviousOffset = 0ULL; for (const auto &Field : S.Fields()) { printPadding(PreviousOffset, Field.Offset()); auto F = getDefinitionTag(S, Field); std::string Result = getModelComment(Field) + getNamedCInstance(*Field.Type(), F); if (not NameBuilder.shouldUseAutomaticName(Field)) { Result += ' ' + ptml::Attributes .getAnnotationString<"_STARTS_AT">(Field.Offset()); } *Out << getCommentableTag(std::move(Result += ';'), S, Field) << '\n'; PreviousOffset = Field.Offset() + Field.Type()->size().value(); } printPadding(PreviousOffset, S.Size()); } *Out << std::move(Suffix) << ";\n"; } void ptml::ModelCBuilder::printDefinition(const model::UnionDefinition &U, std::string &&Suffix) { std::string UnionLine = getModelCommentWithoutLeadingNewline(U) + getKeyword(ptml::CBuilder::Keyword::Union) + " " + ptml::Attributes.getAttributeString<"_PACKED">() + " " + getDefinitionTag(U) + " "; *Out << getCommentableTag(std::move(UnionLine), U); { Scope Scope(*Out, ptml::c::scopes::UnionBody); for (const auto &Field : U.Fields()) { auto F = getDefinitionTag(U, Field); std::string Result = getModelComment(Field) + getNamedCInstance(*Field.Type(), F) + ';'; *Out << getCommentableTag(std::move(Result), U, Field) << '\n'; } } *Out << std::move(Suffix) << ";\n"; } using TD = model::TypedefDefinition; void ptml::ModelCBuilder::printDeclaration(const TD &Typedef) { std::string TypedefString; if (isDeclarationTheSameAsDefinition(Typedef)) TypedefString = getModelCommentWithoutLeadingNewline(Typedef); auto Type = getDefinitionTag(Typedef); TypedefString += getKeyword(ptml::CBuilder::Keyword::Typedef) + " " + getNamedCInstance(*Typedef.UnderlyingType(), Type) + ';'; *Out << getCommentableTag(std::move(TypedefString), Typedef) << '\n'; } /// Generate the definition of a new struct type that wraps all the return /// values of \a F. The name of the struct type is provided by the caller. using RFT = model::RawFunctionDefinition; void ptml::ModelCBuilder::printReturnTypeWrapperDefinition(const RFT &F) { using abi::FunctionType::Layout; auto TheLayout = Layout::make(F); using namespace abi::FunctionType::ReturnMethod; revng_assert(TheLayout.returnMethod() == RegisterSet); std::string StructLine = getWrapperStructComment(F) + getKeyword(ptml::CBuilder::Keyword::Struct) + " " + ptml::Attributes.getAttributeString<"_PACKED">() + " "; StructLine += getArtificialStructTag(F) + " "; *Out << getReturnValueTag(std::move(StructLine), F); { Scope Scope(*Out, ptml::c::scopes::StructBody); for (auto &[Index, ReturnValue] : llvm::enumerate(F.ReturnValues())) { *Out << getModelComment(ReturnValue); auto FieldString = getReturnValueDefinitionTag(F, ReturnValue); auto Line = getNamedCInstance(*ReturnValue.Type(), FieldString) + ';'; *Out << getReturnValueRegisterTag(std::move(Line), F, ReturnValue) << '\n'; } } *Out << ";\n"; } /// Generate the definition of a new struct type that wraps all the return /// values of \a F. The name of the struct type is provided by the caller. using RFT = model::RawFunctionDefinition; void ptml::ModelCBuilder::printReturnTypeWrapperDeclaration(const RFT &F) { using abi::FunctionType::Layout; auto TheLayout = Layout::make(F); using namespace abi::FunctionType::ReturnMethod; revng_assert(TheLayout.returnMethod() == RegisterSet); auto TypeNameReference = getArtificialStructTag(F); *Out << getKeyword(ptml::CBuilder::Keyword::Typedef) << " " << getKeyword(ptml::CBuilder::Keyword::Struct) + " " << ptml::Attributes.getAttributeString<"_PACKED">() << " " << TypeNameReference << " " << TypeNameReference << ";\n"; } /// Print a typedef for a RawFunctionDefinition, that can be used when you have /// a variable that is a pointer to a function. void ptml::ModelCBuilder::printDeclaration(const RFT &F) { *Out << getCommentableTag(getModelCommentWithoutLeadingNewline(F) + getKeyword(ptml::CBuilder::Keyword::Typedef) + " ", F); // In this case, we are defining a type for the function, not the function // itself, so the token right before the parenthesis is the name of the type. printFunctionPrototype(F); *Out << ";\n"; } using CFT = model::CABIFunctionDefinition; /// Print a typedef for a CABI function, that can be used when you have /// a variable that is a pointer to a function. void ptml::ModelCBuilder::printDeclaration(const CFT &F) { *Out << getCommentableTag(getModelCommentWithoutLeadingNewline(F) + getKeyword(ptml::CBuilder::Keyword::Typedef) + " ", F); // In this case, we are defining a type for the function, not the function // itself, so the token right before the parenthesis is the name of the type. printFunctionPrototype(F); *Out << ";\n"; } void ptml::ModelCBuilder::printDeclaration(const model::TypeDefinition &T) { if (auto *Enum = llvm::dyn_cast(&T)) printForwardDeclaration(*Enum); else if (auto *Struct = llvm::dyn_cast(&T)) printForwardDeclaration(*Struct); else if (auto *Union = llvm::dyn_cast(&T)) printForwardDeclaration(*Union); else if (auto *Typedef = llvm::dyn_cast(&T)) printDeclaration(*Typedef); else if (auto *RFT = llvm::dyn_cast(&T)) printDeclaration(*RFT); else if (auto *CFT = llvm::dyn_cast(&T)) printDeclaration(*CFT); else revng_abort("Unsupported type definition."); } void ptml::ModelCBuilder::printDefinition(const model::TypeDefinition &T) { if (isDeclarationTheSameAsDefinition(T)) printDeclaration(T); else if (auto *Struct = llvm::dyn_cast(&T)) printDefinition(*Struct); else if (auto *Union = llvm::dyn_cast(&T)) printDefinition(*Union); else if (auto *Enum = llvm::dyn_cast(&T)) printDefinition(*Enum); else revng_abort("Unsupported type definition."); } static Logger TypePrinterLog{ "type-definition-printer" }; void ptml::ModelCBuilder::printTypeDefinitions() { if (not DependencyCache.has_value()) DependencyCache = DependencyGraph::make(Binary.TypeDefinitions()); std::set Defined; for (const auto *Root : DependencyCache->nodes()) { revng_log(TypePrinterLog, "PostOrder from Root:" << getNodeLabel(Root)); for (const auto *Node : llvm::post_order_ext(Root, Defined)) { LoggerIndent PostOrderIndent{ TypePrinterLog }; revng_log(TypePrinterLog, "post_order visiting: " << getNodeLabel(Node)); const model::TypeDefinition *TypeDefinition = Node->T; if (Configuration.TypesToOmit.contains(TypeDefinition->key())) { revng_log(TypePrinterLog, "Omitted"); continue; } if (Node->isArtificial()) { auto *RF = llvm::cast(TypeDefinition); if (Node->isDeclaration()) { revng_log(TypePrinterLog, "ArtificialDeclaration"); printReturnTypeWrapperDeclaration(*RF); } else { revng_log(TypePrinterLog, "ArtificialDefinition"); printReturnTypeWrapperDefinition(*RF); } } else { if (Node->isDeclaration()) { revng_log(TypePrinterLog, "Declaration"); printDeclaration(*TypeDefinition); } else { revng_log(TypePrinterLog, "Definition"); revng_assert(Node->isDefinition()); revng_assert(not isDeclarationTheSameAsDefinition(*TypeDefinition)); printDefinition(*TypeDefinition); } } *Out << "\n"; } revng_log(TypePrinterLog, "PostOrder DONE"); } } void ptml::ModelCBuilder::printOpaqueTypeDefinition(uint64_t ByteSize) { // Print the typedef inline with the struct definition. std::string StructLine = getKeyword(ptml::CBuilder::Keyword::Typedef) + " " + getKeyword(ptml::CBuilder::Keyword::Struct) + " " + ptml::Attributes.getAttributeString<"_PACKED">() + " " + getOpaqueTypeDeclarationTag(ByteSize) + " "; *Out << std::move(StructLine); { Scope Scope(*Out, ptml::c::scopes::StructBody); // We print padding even when Configuration.EnableExplicitPadding == false // because otherwise the struct is empty and is not valid C99 (empty structs // are a language extension). *Out << tokenTag("uint8_t", ptml::c::tokens::Type) << " " << tokenTag(NameBuilder.paddingFieldName(0), ptml::c::tokens::Field) << "[" << getNumber(ByteSize) << "];\n"; } *Out << " " << getOpaqueTypeDeclarationTag(ByteSize) << ";\n"; } void ptml::ModelCBuilder::printModelOpaqueTypeDefinitions() { std::set ByteSizes = Binary.collectAllTypeSizes(); for (uint64_t ByteSize : ByteSizes) { *Out << "\n"; printOpaqueTypeDefinition(ByteSize); } } using MCB = ptml::ModelCBuilder; using SizeSet = std::set; void MCB::printHelperOpaqueTypeDefinitions(const SizeSet &HelperByteSizes) { std::set ModelByteSizes = Binary.collectAllTypeSizes(); for (uint64_t ByteSize : HelperByteSizes) { if (not ModelByteSizes.contains(ByteSize)) { *Out << "\n"; printOpaqueTypeDefinition(ByteSize); } } }