#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include #include #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/YAMLTraits.h" #include "revng/Pipeline/Analysis.h" #include "revng/Pipeline/ContainerFactory.h" #include "revng/Pipeline/GenericLLVMPipe.h" #include "revng/Pipeline/LLVMContainer.h" #include "revng/Pipeline/Runner.h" #include "revng/Support/Assert.h" #include "revng/TupleTree/Introspection.h" #include "revng/TupleTree/TupleTree.h" namespace pipeline { // The following classes are used to drive the grammar of pipelines YAML files struct ContainerDeclaration { std::string Name; std::string Type; std::string Role = ""; }; struct AnalysisDeclaration { std::string Type; std::vector UsedContainers; std::string Name = ""; std::string Step = ""; }; struct PipeInvocation { std::string Type; std::vector UsedContainers; std::vector Passes = {}; std::vector EnabledWhen = {}; std::string Name = ""; }; struct ArtifactsDeclaration { std::string Container; std::string Kind; std::string SingleTargetFilename; ArtifactsDeclaration() : Container(), Kind(), SingleTargetFilename() {} bool isValid() const { return !Container.empty() && !Kind.empty() && !SingleTargetFilename.empty(); } }; struct StepDeclaration { std::string Name; std::vector Pipes; std::vector EnabledWhen = {}; ArtifactsDeclaration Artifacts = {}; std::vector Analyses = {}; }; struct BranchDeclaration { std::string From; std::vector Steps; }; struct AnalysesListDeclaration { std::string Name; std::vector UsedAnalysesNames; }; struct PipelineDeclaration { std::vector Containers; std::vector Branches; std::vector Analyses = {}; std::vector AnalysesLists = {}; }; class PipelineContext; /// A Loader produces a pipeline runner starting from the YAML version of a /// pipeline. /// /// The loader must be configured with appropriate containers and pipes bindings /// if one wishes to register all those available one can simply use /// Registry::registerAllContainersAndPipes. class Loader { public: using LoaderCallback = std::function; private: llvm::StringMap KnownContainerTypes; llvm::StringMap KnownPipesTypes; llvm::StringMap KnownAnalysisTypes; llvm::StringMap()>> KnownLLVMPipeTypes; llvm::StringMap KnownContainerRoles; std::set EnabledFlags; std::optional OnLLVMContainerCreationAction = std::nullopt; Context *PipelineContext; public: explicit Loader(Context &C) : PipelineContext(&C) {} public: const Context &getContext() const { return *PipelineContext; } Context &getContext() { return *PipelineContext; } public: llvm::Expected load(const BranchDeclaration &) const; llvm::Expected load(const PipelineDeclaration &) const; llvm::Expected load(llvm::ArrayRef) const; llvm::Expected load(llvm::ArrayRef Pipelines) const; template void addDefaultConstructibleContainer(llvm::StringRef Name) { auto [_, inserted] = KnownContainerTypes .try_emplace(Name, ContainerFactory::create()); revng_assert(inserted); } void addContainerFactory(llvm::StringRef Name, ContainerFactory Factory) { KnownContainerTypes.try_emplace(Name, std::move(Factory)); } void addContainerRole(llvm::StringRef Name, llvm::StringRef Role) { KnownContainerRoles.try_emplace(Role, Name.str()); } template void registerLLVMPass(llvm::StringRef Name) { auto [_, inserted] = KnownLLVMPipeTypes.try_emplace(Name, []() { using Type = LLVMPassWrapper; return std::make_unique(LLVMPass()); }); revng_assert(inserted); } template void registerAnalysis(llvm::StringRef Name, const AnalysisType &Analysis) { auto [_, inserted] = KnownAnalysisTypes .try_emplace(Name, AnalysisWrapper::make(Analysis, {})); revng_assert(inserted); } template void registerPipe(llvm::StringRef Name) { auto [_, inserted] = KnownPipesTypes .try_emplace(Name, PipeWrapper::make({})); revng_assert(inserted); } void registerEnabledFlags(auto &NamesRange) { for (const auto &Name : NamesRange) EnabledFlags.insert(Name); } void setLLVMPipeConfigurer(LoaderCallback CallBack) { OnLLVMContainerCreationAction = std::move(CallBack); } public: const llvm::StringMap &getRegisteredPipes() { return KnownPipesTypes; } const llvm::StringMap &getRegisteredAnalyses() { return KnownAnalysisTypes; } private: llvm::Error parseSteps(Runner &Runner, const BranchDeclaration &Declaration, const llvm::StringMap &ReadOnlyNames) const; llvm::Error parseDeclarations(Runner &Runner, const PipelineDeclaration &Declaration, llvm::StringMap &ReadOnlyNames) const; llvm::Error parseStepDeclaration(Runner &Runner, const StepDeclaration &, std::string &LastAddedStep, const llvm::StringMap &ReadOnlyNames) const; llvm::Expected parseInvocation(Step &Step, const PipeInvocation &Invocation, const llvm::StringMap &ReadOnlyNames) const; llvm::Expected parseAnalysis(const AnalysisDeclaration &Declaration) const; llvm::Error parseContainerDeclaration(Runner &Runner, const ContainerDeclaration &, llvm::StringMap &ReadOnlyNames) const; llvm::Expected parseLLVMPass(const PipeInvocation &Invocation) const; llvm::Expected> loadPassFromName(llvm::StringRef Name) const; bool isInvocationUsed(const std::vector &Names) const; }; } // namespace pipeline INTROSPECTION_NS(pipeline, ContainerDeclaration, Name, Type, Role); template<> struct llvm::yaml::MappingTraits { static void mapping(IO &TheIO, pipeline::ContainerDeclaration &Info) { TheIO.mapRequired("Name", Info.Name); TheIO.mapRequired("Type", Info.Type); TheIO.mapOptional("Role", Info.Role); } }; INTROSPECTION_NS(pipeline, AnalysesListDeclaration, Name, UsedAnalysesNames); template<> struct llvm::yaml::MappingTraits { static void mapping(IO &TheIO, pipeline::AnalysesListDeclaration &Info) { TheIO.mapRequired("Name", Info.Name); TheIO.mapOptional("Analyses", Info.UsedAnalysesNames); } }; LLVM_YAML_IS_SEQUENCE_VECTOR(pipeline::ContainerDeclaration) template<> struct llvm::yaml::MappingTraits { static void mapping(IO &TheIO, pipeline::PipeInvocation &Info) { TheIO.mapRequired("Type", Info.Type); TheIO.mapRequired("UsedContainers", Info.UsedContainers); TheIO.mapOptional("Passes", Info.Passes); TheIO.mapOptional("EnabledWhen", Info.EnabledWhen); TheIO.mapOptional("Name", Info.Name); } }; LLVM_YAML_IS_SEQUENCE_VECTOR(pipeline::PipeInvocation) template<> struct llvm::yaml::MappingTraits { static void mapping(IO &TheIO, pipeline::StepDeclaration &Info) { TheIO.mapRequired("Name", Info.Name); TheIO.mapOptional("Pipes", Info.Pipes); TheIO.mapOptional("EnabledWhen", Info.EnabledWhen); TheIO.mapOptional("Artifacts", Info.Artifacts); TheIO.mapOptional("Analyses", Info.Analyses); } }; LLVM_YAML_IS_SEQUENCE_VECTOR(pipeline::StepDeclaration) LLVM_YAML_IS_SEQUENCE_VECTOR(pipeline::AnalysisDeclaration) LLVM_YAML_IS_SEQUENCE_VECTOR(pipeline::BranchDeclaration) LLVM_YAML_IS_SEQUENCE_VECTOR(pipeline::AnalysesListDeclaration) template<> struct llvm::yaml::MappingTraits { static void mapping(IO &TheIO, pipeline::PipelineDeclaration &Info) { TheIO.mapOptional("Containers", Info.Containers); TheIO.mapOptional("Branches", Info.Branches); TheIO.mapOptional("Analyses", Info.Analyses); TheIO.mapOptional("AnalysesLists", Info.AnalysesLists); } }; template<> struct llvm::yaml::MappingTraits { static void mapping(IO &TheIO, pipeline::BranchDeclaration &Info) { TheIO.mapOptional("From", Info.From); TheIO.mapRequired("Steps", Info.Steps); } }; template<> struct llvm::yaml::MappingTraits { static void mapping(IO &TheIO, pipeline::ArtifactsDeclaration &Info) { TheIO.mapRequired("Container", Info.Container); TheIO.mapRequired("Kind", Info.Kind); TheIO.mapRequired("SingleTargetFilename", Info.SingleTargetFilename); } }; template<> struct llvm::yaml::MappingTraits { static void mapping(IO &TheIO, pipeline::AnalysisDeclaration &Info) { TheIO.mapRequired("Name", Info.Name); TheIO.mapRequired("Type", Info.Type); TheIO.mapOptional("Step", Info.Step); TheIO.mapRequired("UsedContainers", Info.UsedContainers); } };