// // This file is distributed under the MIT License. See LICENSE.md for details. // #include "llvm/IR/DebugInfoMetadata.h" #include "revng/PTML/CommentPlacementHelper.h" #include "revng/Pipeline/LLVMContainer.h" #include "revng/Pipeline/Location.h" #include "revng/Pipeline/Pipe.h" #include "revng/Pipeline/RegisterPipe.h" #include "revng/Pipes/DebugInfoHelpers.h" #include "revng/Pipes/Kinds.h" #include "revng/Pipes/Ranks.h" #include "revng/Support/DecompilationHelpers.h" #include "revng/Support/IRBuilder.h" // This name is not present in the emitted C. RegisterIRHelper CommentHelper("comment"); template static ResultSet gatherNonStatementAddresses(const llvm::Instruction &I, ResultSet &&Result = {}) { if (std::optional MaybeAddress = revng::tryExtractAddress(I)) Result.emplace(*MaybeAddress); for (const llvm::Use &V : I.operands()) if (const llvm::Instruction *Cast = llvm::dyn_cast(V)) if (not isStatement(*Cast)) gatherNonStatementAddresses(*Cast, Result); return Result; } template<> struct yield::StatementTraits { using StatementType = llvm::Instruction *; static RangeOf auto getStatements(llvm::BasicBlock *Node) { return *Node | std::views::filter(isStatement) | std::views::transform([](auto &&R) { return &R; }); } static std::set getAddresses(StatementType Statement) { return gatherNonStatementAddresses>(*Statement); } }; struct EmbedStatementComments { public: static constexpr auto Name = "embed-statement-comments"; private: llvm::FunctionCallee makeIRComment(llvm::Module &M) { using StringLiteral = const uint8_t *; llvm::FunctionType &FT = *createFunctionType(M.getContext()); auto Result = getOrInsertIRHelper("comment", M, &FT); auto &Callee = *llvm::cast(Result.getCallee()); Callee.addFnAttr(llvm::Attribute::NoUnwind); Callee.addFnAttr(llvm::Attribute::WillReturn); Callee.addFnAttr(llvm::Attribute::NoMerge); Callee.setDoesNotAccessMemory(); FunctionTags::Comment.addTo(&Callee); return Result; } public: inline std::array getContract() const { return { pipeline::ContractGroup(revng::kinds::StackAccessesSegregated, 0, revng::kinds::StackAccessesSegregated) }; } void run(pipeline::ExecutionContext &Context, pipeline::LLVMContainer &ModuleContainer) { llvm::Module &M = ModuleContainer.getModule(); // Here we should definitely use the builder that checks the debug info, // but since this going to go away soon, let it stay as is. revng::NonDebugInfoCheckingIRBuilder B(M.getContext()); llvm::FunctionCallee IRComment = makeIRComment(M); using TaggedFunctionKind = revng::kinds::TaggedFunctionKind; llvm::StringRef ContainerName = ModuleContainer.name(); for (auto &&[ModelFunction, LLVMFunction] : TaggedFunctionKind::getFunctionsAndCommit(Context, M, ContainerName)) { if (ModelFunction->Comments().empty()) continue; using MapT = yield::DTCommentPlacementHelper; MapT CM(*ModelFunction, *LLVMFunction); auto EmitAComment = [&](llvm::Instruction *Where, const MapT::CommentAssignment &Comment, llvm::StringRef EmittedLocation) { B.SetInsertPoint(Where); std::array Arguments = { llvm::ConstantInt::get(llvm::Type::getInt64Ty(B.getContext()), Comment.CommentIndex), llvm::ConstantInt::get(llvm::Type::getInt8Ty(B.getContext()), Comment.LocationMatchesExactly), getUniqueString(&M, addressesToString(*Comment.ExpectedLocation)), getUniqueString(&M, EmittedLocation) }; auto *Call = B.CreateCall(IRComment, Arguments); Call->copyMetadata(*Where); }; llvm::SmallVector Argumentss; llvm::BasicBlock &EntryBlock = LLVMFunction->getEntryBlock(); for (llvm::BasicBlock *Node : llvm::depth_first(&EntryBlock)) { using Trait = yield::StatementTraits; for (llvm::Instruction *I : Trait::getStatements(Node)) for (const MapT::CommentAssignment &Comment : CM.getComments(I)) EmitAComment(I, Comment, addressesToString(Trait::getAddresses(I))); } for (const MapT::CommentAssignment &Comment : CM.getHomelessComments()) { // For now emit homeless comments at the very top of the function. // TODO: find a better place for them. EmitAComment(&*LLVMFunction->begin()->begin(), Comment, "at the function entry point"); } } } }; static pipeline::RegisterPipe ESCPipe;