#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/GenericLLVMPipe.h" #include "revng/Pipeline/LLVMContainer.h" #include "revng/Pipeline/Runner.h" #include "revng/Pipeline/SavableObject.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; }; struct PipeInvocation { std::string Type; std::vector UsedContainers; std::vector Passes = {}; std::vector EnabledWhen = {}; }; struct ArtifactsDeclaration { std::string Container; std::string Kind; ArtifactsDeclaration() : Container(), Kind() {} bool isValid() const { return !Container.empty() && !Kind.empty(); } }; struct StepDeclaration { std::string Name; std::vector Pipes; std::vector EnabledWhen = {}; ArtifactsDeclaration Artifacts = {}; }; struct PipelineDeclaration { std::string From; std::vector Containers; std::vector Steps; }; 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()>> KnownLLVMPipeTypes; 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 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, [](llvm::StringRef ContainerName) { return std::make_unique(ContainerName); }); revng_assert(inserted); } void addContainerFactory(llvm::StringRef Name, ContainerFactory Factory) { KnownContainerTypes.try_emplace(Name, std::move(Factory)); } 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 registerPipe(llvm::StringRef Name) { const auto LambdaToEmplace = [](std::vector CName) { return PipeWrapper::makeWrapper(std::move(CName)); }; auto [_, inserted] = KnownPipesTypes.try_emplace(Name, LambdaToEmplace); revng_assert(inserted); } template void registerPipe(llvm::StringRef Name, const PipeType &Pipe) { const auto LambdaToEmplace = [Pipe](std::vector ContainerNames) { return PipeWrapper(Pipe, std::move(ContainerNames)); }; auto [_, inserted] = KnownPipesTypes.try_emplace(Name, LambdaToEmplace); revng_assert(inserted); } void registerEnabledFlags(auto &NamesRange) { for (const auto &Name : NamesRange) EnabledFlags.insert(Name); } void setLLVMPipeConfigurer(LoaderCallback CallBack) { OnLLVMContainerCreationAction = std::move(CallBack); } private: llvm::Error parseSteps(Runner &Runner, const PipelineDeclaration &Declaration) const; llvm::Error parseDeclarations(Runner &Runner, const PipelineDeclaration &Declaration) const; llvm::Error parseStepDeclaration(Runner &Runner, const StepDeclaration &, std::string &LastAddedStep) const; llvm::Error parseInvocation(Step &Step, const PipeInvocation &Invocation) const; llvm::Error parseContainerDeclaration(Runner &Runner, const ContainerDeclaration &) const; llvm::Error parseLLVMPass(Step &Step, 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); template<> struct llvm::yaml::MappingTraits : public TupleLikeMappingTraits {}; 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); } }; 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); } }; LLVM_YAML_IS_SEQUENCE_VECTOR(pipeline::StepDeclaration) INTROSPECTION_NS(pipeline, PipelineDeclaration, Containers, Steps); template<> struct llvm::yaml::MappingTraits : public TupleLikeMappingTraits { static void mapping(IO &TheIO, pipeline::PipelineDeclaration &Info) { TheIO.mapOptional("From", Info.From); TheIO.mapRequired("Containers", Info.Containers); 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); } };