// // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include "mlir/IR/Attributes.h" #include "mlir/IR/BuiltinOps.h" #include "mlir/IR/MLIRContext.h" #include "revng/Clift/CliftAttributes.h" #include "revng/Clift/CliftTypes.h" #include "revng/Clift/ModuleVisitor.h" #include "revng/CliftEmitC/CEmitter.h" #include "revng/CliftEmitC/Headers.h" #include "revng/CliftEmitC/TypeDefinitionEmitter.h" #include "revng/CliftEmitC/TypeDependencyGraph.h" #include "revng/CliftImportModel/ImportModel.h" #include "revng/PTML/CTokenEmitter.h" #include "revng/Pipeline/Location.h" #include "revng/Pipes/Ranks.h" class OpaqueTypeSizeCollector : public clift::ModuleVisitor { private: using Base = clift::ModuleVisitor; private: std::set &Result; public: OpaqueTypeSizeCollector(std::set &Result) : Result(Result) { // TODO: do not hard-code this list here. Instead, share it with the model // verification logic (the only other current user of this). Result = { 1, 2, 4, 8, 10, 12, 16 }; } mlir::LogicalResult visitType(mlir::Type Type) { if (uint64_t Size = clift::getObjectSizeOrZero(Type)) Result.emplace(Size); return mlir::success(); } }; class CHeaderEmitterImpl : TypeDefinitionEmitter { public: explicit CHeaderEmitterImpl(ptml::CTokenEmitter &Tokens, const CDataModel &DataModel, TypeEmitterConfiguration Configuration) : TypeDefinitionEmitter(Tokens, DataModel, Configuration) {} public: void emitHeaderPrologue() { // TODO: emit header location *definition*, so that ctrl+click on includes // (references) leads to the correct file. Tokens.emitPragmaOnceDirective(); Tokens.emitNewline(); emitCategoryComment("This header has been generated using rev.ng."); // TODO: emit the license information, revng version information, etc. } void emitCommonIncludes() { // TODO: attach proper header *reference* locations. Tokens.emitIncludeDirective("attributes.h", "", ptml::CTokenEmitter::IncludeMode::Quote); Tokens.emitIncludeDirective("primitive-types.h", "", ptml::CTokenEmitter::IncludeMode::Quote); Tokens.emitNewline(); } private: void emitTypeGraph(const TypeDependencyGraph &Graph) { // In order to improve the printing order, do the visit it in two parts: // first only start from nodes without any successors (real roots), // only then, resolve potential loops by starting from arbitrary nodes. std::unordered_set Emitted; for (const auto *Root : Graph.nodes()) if (not Root->predecessorCount()) emitTypeTree(*Root, Emitted); for (const auto *Root : Graph.nodes()) emitTypeTree(*Root, Emitted); revng_assert(Graph.size() == Emitted.size()); } public: void emitTypes(mlir::ModuleOp Module) { auto Graph = TypeDependencyGraph::makeModelGraph(Module); if (not Graph.empty()) { emitCategoryComment("Types"); emitTypeGraph(Graph); Tokens.emitNewline(); } } private: template void emitFunctionsImpl(mlir::ModuleOp Module, const RankFilter &Rank, llvm::StringRef CategoryComment) { bool CommentEmitted = false; Module->walk([&](clift::FunctionOp Function) { if (pipeline::locationFromString(Rank, Function.getHandle())) { if (not CommentEmitted) { emitCategoryComment(CategoryComment); CommentEmitted = true; } emitFunctionPrototype(Function); Tokens.emitPunctuator(ptml::CTokenEmitter::Punctuator::Semicolon); Tokens.emitNewline(); Tokens.emitNewline(); } }); } public: void emitFunctions(mlir::ModuleOp Module) { emitFunctionsImpl(Module, revng::ranks::Function, "Functions"); } void emitDynamicFunctions(mlir::ModuleOp Module) { emitFunctionsImpl(Module, revng::ranks::DynamicFunction, "Imported Dynamic Functions"); } public: void emitSegments(mlir::ModuleOp Module) { bool CommentEmitted = false; Module->walk([this, &CommentEmitted](clift::GlobalVariableOp Segment) { auto MaybeLocation = pipeline::locationFromString(revng::ranks::Segment, Segment.getHandle()); revng_assert(MaybeLocation.has_value()); if (not CommentEmitted) { emitCategoryComment("Segments"); CommentEmitted = true; } { using RegionKind = ptml::CTokenEmitter::RegionKind; auto Guard = Tokens.enterRegion(RegionKind::Commentable, Segment.getHandle()); emitGlobalDoxygenComment(Segment); using EntityKind = ptml::CTokenEmitter::EntityKind; emitDeclaration(Segment.getType(), CEmitter::DeclaratorInfo{ .Identifier = Segment.getName(), .Location = Segment.getHandle(), .CAttributes = {}, .Kind = EntityKind::GlobalVariable, .Parameters = {} }); Tokens.emitPunctuator(ptml::CTokenEmitter::Punctuator::Semicolon); Tokens.emitNewline(); } Tokens.emitNewline(); }); } template ByteSizeList = std::initializer_list> void emitOpaqueTypes(mlir::MLIRContext &Context, const ByteSizeList &Sizes) { bool CommentEmitted = false; for (uint64_t Size : Sizes) { if (not CommentEmitted) { emitCategoryComment("Opaque types"); CommentEmitted = true; } auto Struct = clift::makeOpaqueStruct(Context, Size); emitClassDefinition(Struct); emitTypeDeclaration(Struct); } Tokens.emitNewline(); } std::set collectOpaqueByteSizes(llvm::ArrayRef Modules) { std::set Result; for (mlir::ModuleOp Module : Modules) { auto R = OpaqueTypeSizeCollector::visit(Module, Result); revng_assert(R.succeeded()); } return Result; } public: void emitHelpers(llvm::ArrayRef Modules) { // TODO: emit `#include`s auto Graph = TypeDependencyGraph::makeHelperGraph(Modules); if (not Graph.empty()) { emitCategoryComment("Types"); revng_assert(!Modules.empty()); emitTypeGraph(Graph); } bool CommentEmitted = false; std::unordered_set EmittedFunctions; for (mlir::ModuleOp Module : Modules) { Module->walk([this, &CommentEmitted, &EmittedFunctions](clift::FunctionOp Function) { if (EmittedFunctions.contains(Function.getHandle())) return; if (pipeline::locationFromString(revng::ranks::HelperFunction, Function.getHandle())) { if (not CommentEmitted) { emitCategoryComment("Functions"); CommentEmitted = true; } emitFunctionPrototype(Function); Tokens.emitPunctuator(ptml::CTokenEmitter::Punctuator::Semicolon); Tokens.emitNewline(); Tokens.emitNewline(); auto [_, Success] = EmittedFunctions.emplace(Function.getHandle()); revng_assert(Success); } }); } Tokens.emitNewline(); } }; void emitCommonIncludes(ptml::CTokenEmitter &Tokens, const CDataModel &DataModel) { CHeaderEmitterImpl Emitter(Tokens, DataModel, TypeEmitterConfiguration{}); Emitter.emitCommonIncludes(); } void emitTypes(ptml::CTokenEmitter &Tokens, mlir::ModuleOp Module, TypeEmitterConfiguration Configuration) { CHeaderEmitterImpl Emitter(Tokens, clift::getDataModel(Module), Configuration); Emitter.emitTypes(Module); } void emitTypeAndGlobalHeader(ptml::CTokenEmitter &Tokens, mlir::ModuleOp Module, TypeEmitterConfiguration Configuration) { CHeaderEmitterImpl Emitter(Tokens, clift::getDataModel(Module), Configuration); Emitter.emitHeaderPrologue(); Emitter.emitCommonIncludes(); // TODO: split the following into separate headers. Emitter.emitTypes(Module); Emitter.emitFunctions(Module); Emitter.emitDynamicFunctions(Module); Emitter.emitSegments(Module); Emitter.emitOpaqueTypes(*Module.getContext(), Emitter.collectOpaqueByteSizes({ Module })); } void emitHelperHeader(ptml::CTokenEmitter &Tokens, llvm::ArrayRef Modules, const model::Binary &Binary) { revng_check(not Modules.empty()); const CDataModel &DataModel = clift::getDataModel(Modules.front()); revng_assert(llvm::all_of(Modules.drop_front(), [&DataModel](mlir::ModuleOp Module) { return clift::getDataModel(Module) == DataModel; })); TypeEmitterConfiguration Configuration = { .ExplicitPadding = true, }; CHeaderEmitterImpl Emitter(Tokens, DataModel, Configuration); Emitter.emitHeaderPrologue(); Emitter.emitHelpers(Modules); // This trick is practically a `const_cast`. mlir::MLIRContext &Context = *mlir::ModuleOp(Modules.front()).getContext(); std::set NonModelOpaqueTypes; std::ranges::set_difference(Emitter.collectOpaqueByteSizes(Modules), Binary.collectAllTypeSizes(), std::inserter(NonModelOpaqueTypes, NonModelOpaqueTypes.end())); Emitter.emitOpaqueTypes(Context, NonModelOpaqueTypes); } void emitSingleTypeDefinition(ptml::CTokenEmitter &Tokens, const CDataModel &DataModel, clift::DefinedType Type, TypeEmitterConfiguration Config) { TypeDefinitionEmitter Emitter(Tokens, DataModel, Config); Emitter.emitTypeDefinition(Type); Tokens.emitNewline(); }