#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include #include #include #include "llvm/ADT/ArrayRef.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/PassRegistry.h" #include "revng/ADT/Concepts.h" #include "revng/Pipeline/ContainerFactorySet.h" #include "revng/Pipeline/Contract.h" #include "revng/Pipeline/LLVMContainer.h" #include "revng/Pipeline/Step.h" #include "revng/Support/Debug.h" #include "revng/Support/ResourceFinder.h" namespace pipeline { class LLVMPassWrapperBase { public: virtual ~LLVMPassWrapperBase() = default; virtual void registerPasses(llvm::legacy::PassManager &Manager) = 0; virtual const std::vector &getContract() const = 0; virtual std::unique_ptr clone() const = 0; virtual llvm::StringRef getName() const = 0; virtual void print(llvm::raw_ostream &OS) const = 0; }; template concept LLVMPass = requires(T P) { { T::Name } -> convertible_to; { P.registerPasses(std::declval()) }; }; template concept LLVMPrintablePass = requires(T P) { { P.print(llvm::outs()) }; }; class PureLLVMPassWrapper : public LLVMPassWrapperBase { private: std::string PassName; public: PureLLVMPassWrapper(llvm::StringRef PassName) : PassName(PassName.str()) {} static bool passExists(llvm::StringRef PassName) { return llvm::PassRegistry::getPassRegistry()->getPassInfo(PassName); } void print(llvm::raw_ostream &OS) const override { OS << "-" << PassName; } static llvm::Expected> create(llvm::StringRef PassName) { if (not passExists(PassName)) return llvm::createStringError(llvm::inconvertibleErrorCode(), "Could not load llvm pass %s ", PassName.str().c_str()); return std::make_unique(PassName); } ~PureLLVMPassWrapper() override = default; void registerPasses(llvm::legacy::PassManager &Manager) override { auto *Registry = llvm::PassRegistry::getPassRegistry(); Manager.add(Registry->getPassInfo(PassName)->createPass()); } const std::vector &getContract() const override { static const std::vector Empty{}; return Empty; } std::unique_ptr clone() const override; llvm::StringRef getName() const override { return PassName; } }; /// LLVM pipes are pipes composed of any number of llvm passes /// /// The contract is deduced by the used passes, and the passes are assembled /// into pass managers when needed. template class LLVMPassWrapper : public LLVMPassWrapperBase { public: using RegistrationFunctionType = void (*)(llvm::legacy::PassManager &); private: T PipePass; std::vector Contract; public: LLVMPassWrapper(T Pass) : PipePass(std::move(Pass)), Contract(this->PipePass.getContract()) {} LLVMPassWrapper(const LLVMPassWrapper &Other) = default; LLVMPassWrapper(LLVMPassWrapper &&Other) = default; template LLVMPassWrapper(ArgsT &&...Args) : PipePass(std::forward(Args)...), Contract(this->PipePass.getContract()) {} ~LLVMPassWrapper() override = default; public: llvm::StringRef getName() const override { return T ::Name; } public: void registerPasses(llvm::legacy::PassManager &Manager) override { PipePass.registerPasses(Manager); } const std::vector &getContract() const override { return Contract; } std::unique_ptr clone() const override { return std::make_unique(*this); } void print(llvm::raw_ostream &OS) const override { if constexpr (LLVMPrintablePass) PipePass.print(OS); else OS << "-" << T::Name; } }; /// Implementation of the LLVM pipes to be instantiated for a particular LLVM /// container template class GenericLLVMPipe { private: llvm::SmallVector, 4> Passes; public: static constexpr auto Name = "GenericLLVMPipe"; template explicit GenericLLVMPipe(T... Pass) { (addPass(std::move(Pass)), ...); } GenericLLVMPipe &operator=(const GenericLLVMPipe &Other) { if (this == &Other) return *this; llvm::SmallVector, 4> NewPasses; for (const auto &P : Other.Passes) NewPasses.push_back(P->clone()); Passes = std::move(NewPasses); return *this; } GenericLLVMPipe(const GenericLLVMPipe &Other) { for (const auto &P : Other.Passes) Passes.push_back(P->clone()); } GenericLLVMPipe &operator=(GenericLLVMPipe &&Other) = default; GenericLLVMPipe(GenericLLVMPipe &&Other) = default; ~GenericLLVMPipe() = default; std::vector getContract() const { std::vector Contract; for (const auto &Element : Passes) for (const auto &C : Element->getContract()) Contract.push_back(C); return Contract; } void run(const Context &, LLVMContainer &Container) { llvm::legacy::PassManager Manager; for (const auto &Element : Passes) Element->registerPasses(Manager); Manager.run(Container.getModule()); } void addPass(const PureLLVMPassWrapper &Pass) { Passes.emplace_back(Pass.clone()); } template void addPass(T Pass) { using Type = LLVMPassWrapper; auto Wrapper = std::make_unique(std::forward(Pass)); Passes.emplace_back(std::move(Wrapper)); } template void emplacePass(ArgsT &&...Args) { using Type = LLVMPassWrapper; auto Wrapper = std::make_unique(std::forward(Args)...); Passes.emplace_back(std::move(Wrapper)); } void addPass(std::unique_ptr Impl) { Passes.emplace_back(std::move(Impl)); } void print(const Context &Ctx, llvm::raw_ostream &OS, llvm::ArrayRef ContainerNames) const { OS << *revng::ResourceFinder.findFile("bin/revng"); OS << " opt --model-path=model.yml " << ContainerNames[0] << " -o " << ContainerNames[0]; for (const auto &Pass : Passes) { Pass->print(OS); OS << " "; } OS << "\n"; } public: template void dump(OStream &OS, size_t Indentation = 0) const { for (const auto &Pass : Passes) { indent(OS, Indentation); OS << Pass->getName().str() << "\n"; } } void dump() const debug_function { dump(dbg); } }; class O2Pipe { public: static constexpr auto Name = "O2"; std::vector getContract() const { return {}; } void registerPasses(llvm::legacy::PassManager &Manager); }; using LLVMPipe = GenericLLVMPipe; } // namespace pipeline