Files
2025-07-23 11:38:53 +02:00

322 lines
10 KiB
C++

#pragma once
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include <memory>
#include <optional>
#include <string>
#include <vector>
#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<std::string> UsedContainers;
std::string Name;
std::string Step;
std::string Docs;
};
struct PipeInvocation {
std::string Type;
std::vector<std::string> UsedContainers;
std::vector<std::string> Passes = {};
std::vector<std::string> EnabledWhen = {};
std::string Name = "";
};
struct ArtifactsDeclaration {
std::string Container;
std::string Kind;
std::string SingleTargetFilename;
std::string Docs;
ArtifactsDeclaration() : Container(), Kind(), SingleTargetFilename() {}
bool isValid() const {
return !Container.empty() && !Kind.empty() && !SingleTargetFilename.empty();
}
};
struct StepDeclaration {
std::string Name;
std::vector<PipeInvocation> Pipes;
std::vector<std::string> EnabledWhen = {};
ArtifactsDeclaration Artifacts = {};
std::vector<AnalysisDeclaration> Analyses = {};
};
struct BranchDeclaration {
std::string From;
std::vector<StepDeclaration> Steps;
};
struct AnalysesListDeclaration {
std::string Name;
std::vector<std::string> UsedAnalysesNames;
};
struct PipelineDeclaration {
std::string Component;
std::vector<ContainerDeclaration> Containers;
std::vector<BranchDeclaration> Branches;
std::vector<AnalysisDeclaration> Analyses = {};
std::vector<AnalysesListDeclaration> 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<llvm::Error(const Loader &, LLVMPipe &)>;
private:
llvm::StringMap<ContainerFactory> KnownContainerTypes;
llvm::StringMap<PipeWrapper> KnownPipesTypes;
llvm::StringMap<AnalysisWrapper> KnownAnalysisTypes;
llvm::StringMap<std::function<std::unique_ptr<LLVMPassWrapperBase>()>>
KnownLLVMPipeTypes;
llvm::StringMap<std::string> KnownContainerRoles;
std::set<std::string> EnabledFlags;
std::optional<LoaderCallback> OnLLVMContainerCreationAction = std::nullopt;
Context *PipelineContext = nullptr;
public:
explicit Loader(Context &C) : PipelineContext(&C) {}
public:
const Context &getContext() const { return *PipelineContext; }
Context &getContext() { return *PipelineContext; }
public:
llvm::Expected<Runner> load(const BranchDeclaration &) const;
llvm::Expected<Runner> load(const PipelineDeclaration &) const;
llvm::Expected<Runner> load(llvm::ArrayRef<PipelineDeclaration>) const;
llvm::Expected<Runner> load(llvm::ArrayRef<std::string> Pipelines) const;
template<typename ContainerType>
void addDefaultConstructibleContainer(llvm::StringRef Name) {
bool S = KnownContainerTypes
.try_emplace(Name, ContainerFactory::create<ContainerType>())
.second;
revng_assert(S);
}
void addContainerFactory(llvm::StringRef Name, ContainerFactory Factory) {
KnownContainerTypes.try_emplace(Name, std::move(Factory));
}
const ContainerFactory &
getContainerFactory(llvm::StringRef ContainerName) const {
return KnownContainerTypes.find(ContainerName)->second;
}
void addContainerRole(llvm::StringRef Name, llvm::StringRef Role) {
KnownContainerRoles.try_emplace(Role, Name.str());
}
const llvm::StringMap<PipeWrapper> &getPipes() const {
return KnownPipesTypes;
}
template<typename LLVMPass>
void registerLLVMPass(llvm::StringRef Name) {
auto &&[_, inserted] = KnownLLVMPipeTypes.try_emplace(Name, []() {
using Type = LLVMPassWrapper<LLVMPass>;
return std::make_unique<Type>(LLVMPass());
});
revng_assert(inserted);
}
template<typename AnalysisType>
void registerAnalysis(llvm::StringRef Name, const AnalysisType &Analysis) {
bool S = KnownAnalysisTypes
.try_emplace(Name, AnalysisWrapper::make(Analysis, {}))
.second;
revng_assert(S);
}
template<typename PipeType>
void registerPipe(llvm::StringRef Name) {
bool S = KnownPipesTypes.try_emplace(Name, PipeWrapper::make<PipeType>({}))
.second;
revng_assert(S);
}
void registerEnabledFlags(auto &NamesRange) {
for (const auto &Name : NamesRange)
EnabledFlags.insert(Name);
}
void setLLVMPipeConfigurer(LoaderCallback CallBack) {
OnLLVMContainerCreationAction = std::move(CallBack);
}
public:
const llvm::StringMap<PipeWrapper> &getRegisteredPipes() {
return KnownPipesTypes;
}
const llvm::StringMap<AnalysisWrapper> &getRegisteredAnalyses() {
return KnownAnalysisTypes;
}
private:
llvm::Error parseSteps(Runner &Runner,
const BranchDeclaration &Declaration,
const llvm::StringMap<std::string> &ReadOnlyNames,
const llvm::StringRef Component) const;
llvm::Error
parseDeclarations(Runner &Runner,
const PipelineDeclaration &Declaration,
llvm::StringMap<std::string> &ReadOnlyNames) const;
llvm::Error
parseStepDeclaration(Runner &Runner,
const StepDeclaration &,
std::string &LastAddedStep,
const llvm::StringMap<std::string> &ReadOnlyNames,
const llvm::StringRef Component) const;
llvm::Expected<PipeWrapper>
parseInvocation(Step &Step,
const PipeInvocation &Invocation,
const llvm::StringMap<std::string> &ReadOnlyNames) const;
llvm::Expected<AnalysisWrapper>
parseAnalysis(const AnalysisDeclaration &Declaration) const;
llvm::Error
parseContainerDeclaration(Runner &Runner,
const ContainerDeclaration &,
llvm::StringMap<std::string> &ReadOnlyNames) const;
llvm::Expected<PipeWrapper>
parseLLVMPass(const PipeInvocation &Invocation) const;
llvm::Expected<std::unique_ptr<LLVMPassWrapperBase>>
loadPassFromName(llvm::StringRef Name) const;
bool isInvocationUsed(const std::vector<std::string> &Names) const;
};
} // namespace pipeline
INTROSPECTION_NS(pipeline, ContainerDeclaration, Name, Type, Role);
template<>
struct llvm::yaml::MappingTraits<pipeline::ContainerDeclaration> {
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<pipeline::AnalysesListDeclaration> {
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<pipeline::PipeInvocation> {
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<pipeline::StepDeclaration> {
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<pipeline::PipelineDeclaration> {
static void mapping(IO &TheIO, pipeline::PipelineDeclaration &Info) {
TheIO.mapRequired("Component", Info.Component);
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<pipeline::BranchDeclaration> {
static void mapping(IO &TheIO, pipeline::BranchDeclaration &Info) {
TheIO.mapOptional("From", Info.From);
TheIO.mapRequired("Steps", Info.Steps);
}
};
template<>
struct llvm::yaml::MappingTraits<pipeline::ArtifactsDeclaration> {
static void mapping(IO &TheIO, pipeline::ArtifactsDeclaration &Info) {
TheIO.mapRequired("Container", Info.Container);
TheIO.mapRequired("Kind", Info.Kind);
TheIO.mapRequired("Docs", Info.Docs);
TheIO.mapRequired("SingleTargetFilename", Info.SingleTargetFilename);
}
};
template<>
struct llvm::yaml::MappingTraits<pipeline::AnalysisDeclaration> {
static void mapping(IO &TheIO, pipeline::AnalysisDeclaration &Info) {
TheIO.mapRequired("Name", Info.Name);
TheIO.mapRequired("Type", Info.Type);
TheIO.mapOptional("Step", Info.Step);
TheIO.mapOptional("Docs", Info.Docs);
TheIO.mapRequired("UsedContainers", Info.UsedContainers);
}
};