diff --git a/include/revng/HeadersGeneration/PTMLHeaderBuilder.h b/include/revng/HeadersGeneration/PTMLHeaderBuilder.h index e8146fc32..069352e7d 100644 --- a/include/revng/HeadersGeneration/PTMLHeaderBuilder.h +++ b/include/revng/HeadersGeneration/PTMLHeaderBuilder.h @@ -33,7 +33,7 @@ public: /// Generate a C header containing a serialization of the type system, /// i.e. function prototypes, structs, unions, typedefs, and anything that /// resides in the model. - bool printModelHeader(); + bool printModelHeader(bool DefineOpaqueTypes = false); /// Generate a C header containing the declaration of each non-isolated /// function in a given LLVM IR module, i.e. QEMU helpers and revng helpers, diff --git a/include/revng/TypeNames/ModelCBuilder.h b/include/revng/TypeNames/ModelCBuilder.h index bec666950..ae2e45ed7 100644 --- a/include/revng/TypeNames/ModelCBuilder.h +++ b/include/revng/TypeNames/ModelCBuilder.h @@ -873,6 +873,20 @@ public: /// on every type, as types can depend on each other. /// This method ensures they are printed in a valid order. void printTypeDefinitions(); + + /// Print all opaque type definitions required by the types in the model. + void printModelOpaqueTypeDefinitions(); + + /// Print all opaque type definitions required by the types in the headers, + /// that are not otherwise printed by printModelOpaqueTypeDefinitions. + void + printHelperOpaqueTypeDefinitions(const std::set &HelperByteSizes); + +private: + std::set getModelOpaqueByteSizes(); + + /// Print a single opaque type definitions with given ByteSize + void printOpaqueTypeDefinition(uint64_t ByteSize); }; } // namespace ptml diff --git a/lib/Backend/DecompileToDirectoryPipe.cpp b/lib/Backend/DecompileToDirectoryPipe.cpp index d6524ac7d..7c605c82d 100644 --- a/lib/Backend/DecompileToDirectoryPipe.cpp +++ b/lib/Backend/DecompileToDirectoryPipe.cpp @@ -80,7 +80,7 @@ void DecompileToDirectory::run(pipeline::ExecutionContext &EC, B.setOutputStream(Out); ptml::HeaderBuilder HB = B; - HB.printModelHeader(); + HB.printModelHeader(/*DefineOpaqueTypes*/ true); Out.flush(); diff --git a/lib/HeadersGeneration/HelpersToHeader.cpp b/lib/HeadersGeneration/HelpersToHeader.cpp index 364837f50..861f827e0 100644 --- a/lib/HeadersGeneration/HelpersToHeader.cpp +++ b/lib/HeadersGeneration/HelpersToHeader.cpp @@ -95,6 +95,8 @@ bool ptml::HeaderBuilder::printHelpersHeader(const llvm::Module &M) { + B.getIncludeQuote("primitive-types.h") + "\n"; B.append(std::move(Includes)); + std::set OpaqueByteSizes; + for (const llvm::Function &F : M.functions()) { // Skip non-helpers @@ -140,6 +142,10 @@ bool ptml::HeaderBuilder::printHelpersHeader(const llvm::Module &M) { const auto *RetTy = F.getReturnType(); if (auto *RetStructTy = dyn_cast(RetTy)) { printLLVMTypeDeclaration(RetStructTy, F, B); + const auto &DataLayout = F.getParent()->getDataLayout(); + + OpaqueByteSizes + .insert(DataLayout.getStructLayout(RetStructTy)->getSizeInBytes()); B.append("\n"); } @@ -148,8 +154,22 @@ bool ptml::HeaderBuilder::printHelpersHeader(const llvm::Module &M) { } printHelperPrototype(&F, B); + B.append("\n"); } + // Always print the opaque type definitions here. + // The helper header is only used if we have the function body, which will + // need these definitions. + B.appendLineComment("\\defgroup Opaque Types"); + B.appendLineComment("\\{"); + + auto OpaqueTypeScope = B.getScopeTag(ptml::tags::Div); + + B.printHelperOpaqueTypeDefinitions(OpaqueByteSizes); + + B.appendLineComment("\\}"); + B.append("\n"); + return true; } diff --git a/lib/HeadersGeneration/ModelToHeader.cpp b/lib/HeadersGeneration/ModelToHeader.cpp index fc5c1d5bb..09b7e8ed1 100644 --- a/lib/HeadersGeneration/ModelToHeader.cpp +++ b/lib/HeadersGeneration/ModelToHeader.cpp @@ -27,7 +27,7 @@ static Logger Log{ "model-to-header" }; -bool ptml::HeaderBuilder::printModelHeader() { +bool ptml::HeaderBuilder::printModelHeader(bool DefineOpaqueTypes) { auto Scope = B.getScopeTag(ptml::tags::Div); @@ -128,13 +128,32 @@ bool ptml::HeaderBuilder::printModelHeader() { B.appendLineComment("\\defgroup Segments"); B.appendLineComment("\\{"); - for (const model::Segment &Segment : B.Binary.Segments()) + for (const model::Segment &Segment : B.Binary.Segments()) { + if (Segment.Type().isEmpty()) + continue; + auto &SegmentType = Segment.Type()->toStruct(); + if (B.Configuration.TypesToOmit.contains(SegmentType.key())) + continue; + B.printSegmentType(Segment); + } B.append("\n"); B.appendLineComment("\\}"); B.append("\n"); } + if (DefineOpaqueTypes) { + B.appendLineComment("\\defgroup Opaque Types"); + B.appendLineComment("\\{"); + + auto Scope = B.getScopeTag(ptml::tags::Div); + + B.printModelOpaqueTypeDefinitions(); + + B.appendLineComment("\\}"); + B.append("\n"); + } + return true; } diff --git a/lib/HeadersGeneration/ModelToHeaderPipe.cpp b/lib/HeadersGeneration/ModelToHeaderPipe.cpp index 343c32bb8..47715924b 100644 --- a/lib/HeadersGeneration/ModelToHeaderPipe.cpp +++ b/lib/HeadersGeneration/ModelToHeaderPipe.cpp @@ -60,8 +60,7 @@ public: { .EnableStackFrameInlining = options::EnableStackFrameInlining, .EnablePrintingOfTheMaximumEnumValue = true, .ExplicitTargetPointerSize = getExplicitPointerSize(Model) }); - ptml::HeaderBuilder(B).printModelHeader(); - + ptml::HeaderBuilder(B).printModelHeader(/*DefineOpaqueTypes*/ true); Header.flush(); ErrorCode = Header.error(); if (ErrorCode) @@ -95,7 +94,7 @@ void ModelToHeader::run() { { .EnableStackFrameInlining = revng::options::EnableStackFrameInlining, .EnablePrintingOfTheMaximumEnumValue = true, .ExplicitTargetPointerSize = getExplicitPointerSize(Binary) }); - ptml::HeaderBuilder(B).printModelHeader(); + ptml::HeaderBuilder(B).printModelHeader(/*DefineOpaqueTypes*/ true); Out->flush(); } diff --git a/lib/TypeNames/TypePrinters.cpp b/lib/TypeNames/TypePrinters.cpp index c7477e49a..20e0e6185 100644 --- a/lib/TypeNames/TypePrinters.cpp +++ b/lib/TypeNames/TypePrinters.cpp @@ -321,3 +321,100 @@ void ptml::ModelCBuilder::printTypeDefinitions() { 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); + + std::string UInt8T = getPrimitiveTag(model::PrimitiveKind::Unsigned, 1); + std::string + ArrayName = getOpaqueTypeFieldTag(ByteSize); + *Out << UInt8T << " " << ArrayName << "[" << getNumber(ByteSize) << "];\n"; + } + + *Out << " " << getOpaqueTypeDeclarationTag(ByteSize) + << ";\n"; +} + +std::set ptml::ModelCBuilder::getModelOpaqueByteSizes() { + + std::set ByteSizes = { 1, 2, 4, 8, 10, 12, 16 }; + + for (const model::UpcastableTypeDefinition &Type : Binary.TypeDefinitions()) { + uint64_t ByteSize = Type->size().value_or(0); + if (ByteSize) + ByteSizes.insert(ByteSize); + + llvm::SmallVector Dependencies; + + const model::TypeDefinition *Definition = Type->tryGetAsDefinition(); + if (not Definition) + continue; + + if (const auto *TD = llvm::dyn_cast(Definition)) + Dependencies.push_back(TD->UnderlyingType()); + + if (const auto *S = Definition->getStruct()) + for (const auto &Field : S->Fields()) + Dependencies.push_back(Field.Type()); + + if (const auto *U = Definition->getUnion()) + for (const auto &Field : U->Fields()) + Dependencies.push_back(Field.Type()); + + if (const auto *R = Definition->getRawFunction()) { + for (const auto &A : R->Arguments()) + Dependencies.push_back(A.Type()); + + uint64_t ReturnTypeSize = 0; + for (const auto &RV : R->ReturnValues()) { + Dependencies.push_back(RV.Type()); + ReturnTypeSize += RV.Type()->size().value_or(0); + } + if (ReturnTypeSize) + ByteSizes.insert(ReturnTypeSize); + } + + if (const auto *C = Definition->getCABIFunction()) { + for (const auto &A : C->Arguments()) + Dependencies.push_back(A.Type()); + if (not C->ReturnType().isEmpty()) + Dependencies.push_back(C->ReturnType()); + } + + for (const model::UpcastableType &D : Dependencies) + if (uint64_t ByeSize = D->trySize().value_or(0)) + ByteSizes.insert(ByteSize); + } + + return ByteSizes; +} + +void ptml::ModelCBuilder::printModelOpaqueTypeDefinitions() { + std::set ByteSizes = getModelOpaqueByteSizes(); + 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 = getModelOpaqueByteSizes(); + for (uint64_t ByteSize : HelperByteSizes) { + if (not ModelByteSizes.contains(ByteSize)) { + *Out << "\n"; + printOpaqueTypeDefinition(ByteSize); + } + } +}