#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include "llvm/ADT/Twine.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" #include "llvm/IR/LLVMContext.h" #include "llvm/Support/ModRef.h" #include "revng/ADT/Concepts.h" #include "revng/Support/Assert.h" #include "revng/Support/IRHelpers.h" #include "revng/Support/Tag.h" template concept PointerToLLVMTypeOrDerived = std::derived_from, llvm::Type>; template class OpaqueFunctionsPool { private: llvm::Module *M; const bool PurgeOnDestruction; std::map Pool; llvm::AttributeList AttributeSets; llvm::MemoryEffects MemoryEffects = llvm::MemoryEffects::none(); FunctionTags::TagsSet Tags; public: OpaqueFunctionsPool(llvm::Module *M, bool PurgeOnDestruction) : M(M), PurgeOnDestruction(PurgeOnDestruction) {} ~OpaqueFunctionsPool() { if (PurgeOnDestruction) { for (auto &[Key, F] : Pool) { revng_assert(F->use_begin() == F->use_end()); eraseFromParent(F); } } } public: void addFnAttribute(llvm::Attribute::AttrKind Kind) { using namespace llvm; AttributeSets = AttributeSets.addFnAttribute(M->getContext(), Kind); } void setMemoryEffects(const llvm::MemoryEffects &NewMemoryEffects) { MemoryEffects = NewMemoryEffects; } void setTags(const FunctionTags::TagsSet &Tags) { this->Tags = Tags; } public: auto begin() const { return Pool.begin(); } auto end() const { return Pool.end(); } public: void record(KeyT Key, llvm::Function *F) { auto It = Pool.find(Key); if (It == Pool.end()) { Pool[Key] = F; } else { if (It->second != F) { dbg << "Same key, different function.\nOriginal:\n"; It->second->dump(); dbg << "New:\n"; F->dump(); revng_abort(); } } } public: llvm::Function * get(KeyT Key, llvm::FunctionType *FT, const llvm::Twine &Name = {}) { using namespace llvm; Function *F = nullptr; auto It = Pool.find(Key); if (It != Pool.end()) { F = It->second; } else { F = Function::Create(FT, GlobalValue::ExternalLinkage, Name, M); F->setAttributes(AttributeSets); F->setMemoryEffects(MemoryEffects); Tags.set(F); Pool.insert(It, { Key, F }); } // Ensure the function we're returning is as expected revng_assert(F->getFunctionType() == FT); return F; } llvm::Function *get(KeyT Key, llvm::Type *ReturnType = nullptr, llvm::ArrayRef Arguments = {}, const llvm::Twine &Name = {}) { using namespace llvm; if (ReturnType == nullptr) ReturnType = Type::getVoidTy(M->getContext()); return get(Key, FunctionType::get(ReturnType, Arguments, false), Name); } /// Initialize the pool with all the functions in M that match the tag TheTag, /// using the return type as key. void initializeFromReturnType(const FunctionTags::Tag &TheTag) requires std::derived_from, llvm::Type> { using TypeLike = std::remove_pointer_t; for (llvm::Function &F : TheTag.functions(M)) { auto *RetType = F.getFunctionType()->getReturnType(); if (auto *KeyType = dyn_cast(RetType)) record(KeyType, &F); } } /// Initialize the pool with all the functions in M that match the tag TheTag, /// using the type of the ArgNo-th argument as key. void initializeFromNthArgType(const FunctionTags::Tag &TheTag, unsigned ArgNo) requires std::derived_from, llvm::Type> { using TypeLike = std::remove_pointer_t; for (llvm::Function &F : TheTag.functions(M)) { auto ArgType = F.getFunctionType()->getParamType(ArgNo); if (auto *KeyType = dyn_cast(ArgType)) record(KeyType, &F); } } /// Initialize the pool with all the functions in M that match the tag TheTag, /// using the type of the ArgNo-th argument as key. void initializeFromName(const FunctionTags::Tag &TheTag) requires std::is_same_v { for (llvm::Function &F : TheTag.functions(M)) record(F.getName().str(), &F); } }; enum class InitializationMode { InitializeFromArgument0 = 0, InitializeFromArgument1 = 1, InitializeFromArgument2 = 2, InitializeFromArgument3 = 3, InitializeFromArgument4 = 4, InitializeFromArgument5 = 5, InitializeFromArgument6 = 6, InitializeFromReturnType, CustomInitialization }; template class FunctionPoolTag : public FunctionTags::Tag { public: using AttributeVector = llvm::SmallVector; using CustomInitializer = void (*)(OpaqueFunctionsPool &, llvm::Module &, const FunctionPoolTag &); private: AttributeVector Attributes; llvm::MemoryEffects MemoryEffects; std::set Tags; InitializationMode Initialization; CustomInitializer Initializer = nullptr; private: FunctionPoolTag(llvm::StringRef Name, AttributeVector Attributes, llvm::MemoryEffects MemoryEffects, std::set OtherTags, InitializationMode Initialization, CustomInitializer Initializer) : Tag(Name), Attributes(Attributes), MemoryEffects(MemoryEffects), Tags(OtherTags), Initialization(Initialization), Initializer(Initializer) { Tags.insert(this); } public: FunctionPoolTag(llvm::StringRef Name, AttributeVector Attributes, llvm::MemoryEffects MemoryEffects, std::set OtherTags, InitializationMode Initialization) : FunctionPoolTag(Name, Attributes, MemoryEffects, OtherTags, Initialization, nullptr) { revng_assert(Initialization != InitializationMode::CustomInitialization); } FunctionPoolTag(llvm::StringRef Name, AttributeVector Attributes, llvm::MemoryEffects MemoryEffects, std::set OtherTags, CustomInitializer Initializer) : FunctionPoolTag(Name, Attributes, MemoryEffects, OtherTags, InitializationMode::CustomInitialization, Initializer) {} public: OpaqueFunctionsPool getPool(llvm::Module &M) const { OpaqueFunctionsPool Result(&M, false); // Set attributes for (auto Attribute : Attributes) Result.addFnAttribute(Attribute); Result.setMemoryEffects(MemoryEffects); // Set revng tags FunctionTags::TagsSet TagsSet; for (const Tag *TheTag : Tags) TagsSet.insert(*TheTag); Result.setTags(TagsSet); auto CustomInitialization = InitializationMode::CustomInitialization; bool HasInitializer = Initializer != nullptr; revng_check((Initialization == CustomInitialization) == HasInitializer); if constexpr (std::derived_from, llvm::Type>) { switch (Initialization) { case InitializationMode::InitializeFromReturnType: Result.initializeFromReturnType(*this); return Result; case InitializationMode::InitializeFromArgument0: case InitializationMode::InitializeFromArgument1: case InitializationMode::InitializeFromArgument2: case InitializationMode::InitializeFromArgument3: case InitializationMode::InitializeFromArgument4: case InitializationMode::InitializeFromArgument5: case InitializationMode::InitializeFromArgument6: { auto ArgNumber = static_cast(Initialization); Result.initializeFromNthArgType(*this, ArgNumber); return Result; } case InitializationMode::CustomInitialization: // Handle below break; } } revng_check(Initialization == InitializationMode::CustomInitialization); Initializer(Result, M, *this); return Result; } };