Files
Ivan Krysak 25538385c8 Avoid using !! unless necessary
The only necessary place is when passing it into the  `BOOST_TEST`
macro, which doesn't convert its argument to `bool` internally.
2025-10-13 10:26:41 +03:00

368 lines
12 KiB
C++

/// \file Loader.cpp
/// A loader is a object that accepts a serialized pipeline and yields a runner
/// object that rappresents that pipeline.
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include <optional>
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include "revng/Pipeline/Loader.h"
#include "revng/Pipeline/Pipe.h"
#include "revng/Pipeline/Runner.h"
using namespace pipeline;
using namespace std;
using namespace llvm;
using StringsMap = llvm::StringMap<string>;
Error Loader::parseStepDeclaration(Runner &Runner,
const StepDeclaration &Declaration,
std::string &LastAddedStep,
const StringsMap &ReadOnlyNames,
const llvm::StringRef Component) const {
auto &JustAdded = Runner.emplaceStep(LastAddedStep,
Declaration.Name,
Component);
LastAddedStep = Declaration.Name;
if (Declaration.Artifacts.isValid()) {
auto &KindName = Declaration.Artifacts.Kind;
const Kind *Kind = Runner.getKindsRegistry().find(KindName);
if (Kind == nullptr) {
return revng::createError(Twine("Can't find kind ") + KindName
+ Twine(" for the artifact of step ")
+ LastAddedStep + Twine(" not found"));
}
if (auto Error = JustAdded.setArtifacts(Declaration.Artifacts.Container,
Kind,
Declaration.Artifacts
.SingleTargetFilename);
Error) {
return Error;
}
}
for (const auto &Invocation : Declaration.Pipes) {
if (not isInvocationUsed(Invocation.EnabledWhen))
continue;
auto MaybeInvocation = parseInvocation(JustAdded,
Invocation,
ReadOnlyNames);
if (not MaybeInvocation)
return MaybeInvocation.takeError();
JustAdded.addPipe(std::move(*MaybeInvocation));
}
for (const auto &SingleAnalysis : Declaration.Analyses) {
auto MaybeInvocation = parseAnalysis(SingleAnalysis);
if (!MaybeInvocation)
return MaybeInvocation.takeError();
JustAdded.addAnalysis(SingleAnalysis.Name, std::move(*MaybeInvocation));
}
return Error::success();
}
llvm::Expected<std::unique_ptr<LLVMPassWrapperBase>>
Loader::loadPassFromName(llvm::StringRef Name) const {
auto It = KnownLLVMPipeTypes.find(Name);
if (It != KnownLLVMPipeTypes.end())
return It->second();
return PureLLVMPassWrapper::create(Name);
}
llvm::Expected<PipeWrapper>
Loader::parseLLVMPass(const PipeInvocation &Invocation) const {
LLVMPipe ToInsert;
if (OnLLVMContainerCreationAction.has_value()) {
auto MaybeError = (*OnLLVMContainerCreationAction)(*this, ToInsert);
if (MaybeError)
return std::move(MaybeError);
}
for (const auto &PassName : Invocation.Passes) {
auto MaybePass = loadPassFromName(PassName);
if (not MaybePass)
return MaybePass.takeError();
ToInsert.addPass(std::move(*MaybePass));
}
return PipeWrapper::make(std::move(ToInsert), Invocation.UsedContainers);
}
llvm::Expected<AnalysisWrapper>
Loader::parseAnalysis(const AnalysisDeclaration &Declaration) const {
auto It = KnownAnalysisTypes.find(Declaration.Type);
if (It == KnownAnalysisTypes.end()) {
auto *Message = "While parsing analyses: no known analysis named name %s";
return revng::createError(Message, Declaration.Type.c_str());
}
auto &Entry = It->second;
auto ToReturn = AnalysisWrapper(Entry, Declaration.UsedContainers);
ToReturn->setUserBoundName(Declaration.Name);
return ToReturn;
}
llvm::Expected<PipeWrapper>
Loader::parseInvocation(Step &Step,
const PipeInvocation &Invocation,
const StringsMap &ReadOnlyNames) const {
if (Invocation.Type == "llvm-pipe")
return parseLLVMPass(Invocation);
if (not Invocation.Passes.empty())
return revng::createError("While parsing pipe %s: passes declarations are "
"not allowed in non-llvm pipes",
Invocation.Type.c_str());
auto It = KnownPipesTypes.find(Invocation.Type);
if (It == KnownPipesTypes.end()) {
auto *Message = "While parsing pipe invocation: no known pipe with "
"name %s";
return revng::createError(Message, Invocation.Type.c_str());
}
auto &Pipe = It->second;
for (const auto &ContainerNameAndIndex :
llvm::enumerate(Invocation.UsedContainers)) {
const auto &ContainerName = ContainerNameAndIndex.value();
size_t Index = ContainerNameAndIndex.index();
if (ReadOnlyNames.find(ContainerName) == ReadOnlyNames.end())
continue;
const auto &RoleName = ReadOnlyNames.find(ContainerName)->second;
if (Pipe.Pipe->isContainerArgumentConst(Index))
continue;
if (PipelineContext->hasRegisteredReadOnlyContainer(ContainerName)) {
return revng::createError("Detected two non const uses of read only "
"container %s",
ContainerName.c_str());
}
revng_assert(Step.containers().containsOrCanCreate(ContainerName));
const auto &Container = *Step.containers().find(ContainerName);
PipelineContext->addReadOnlyContainer(RoleName, Container);
}
return PipeWrapper(Pipe, Invocation.UsedContainers);
}
using BCDecl = ContainerDeclaration;
Error Loader::parseContainerDeclaration(Runner &Pipeline,
const BCDecl &Dec,
StringsMap &ReadOnlyNames) const {
if (not Dec.Role.empty() and KnownContainerRoles.count(Dec.Role) == 0) {
auto *Message = "While parsing container declaration with Name %s has a "
"unknown role %s.";
return revng::createError(Message, Dec.Name.c_str(), Dec.Role.c_str());
}
if (not Dec.Role.empty()
and KnownContainerRoles.find(Dec.Role)->second != Dec.Type) {
auto *Message = "While parsing container declaration with Name %s: "
"role %s was not a valid role for container of type %s.";
return revng::createError(Message,
Dec.Name.c_str(),
Dec.Role.c_str(),
Dec.Type.c_str());
}
auto It = KnownContainerTypes.find(Dec.Type);
if (It == KnownContainerTypes.end()) {
auto *Message = "While parsing container declaration: No known container "
"with name %s";
return revng::createError(Message, Dec.Type.c_str());
}
auto &Entry = It->second;
Pipeline.addContainerFactory(Dec.Name, Entry);
if (not Dec.Role.empty())
ReadOnlyNames[Dec.Name] = Dec.Role;
return Error::success();
}
llvm::Expected<Runner>
Loader::load(llvm::ArrayRef<std::string> Pipelines) const {
std::vector<PipelineDeclaration> Declarations(Pipelines.size());
for (size_t I = 0; I < Pipelines.size(); I++) {
yaml::Input Input(Pipelines[I]);
Input >> Declarations[I];
if (Input.error())
return revng::createError("Could not parse pipeline\n");
}
return load(Declarations);
}
llvm::Error Loader::parseSteps(Runner &Runner,
const BranchDeclaration &Declaration,
const StringsMap &ReadOnlyNames,
const llvm::StringRef Component) const {
std::string LastAddedStep = Declaration.From.empty() ? "begin" :
Declaration.From;
for (const auto &Step : Declaration.Steps) {
if (not isInvocationUsed(Step.EnabledWhen))
continue;
if (auto Error = parseStepDeclaration(Runner,
Step,
LastAddedStep,
ReadOnlyNames,
Component);
Error)
return Error;
}
return llvm::Error::success();
}
llvm::Error Loader::parseDeclarations(Runner &Runner,
const PipelineDeclaration &Declaration,
StringsMap &ReadOnlyNames) const {
for (const auto &Container : Declaration.Containers)
if (auto Error = parseContainerDeclaration(Runner,
Container,
ReadOnlyNames);
Error)
return Error;
return llvm::Error::success();
}
static bool requirementsMet(llvm::ArrayRef<const BranchDeclaration *> Scheduled,
const BranchDeclaration &ToSchedule) {
if (ToSchedule.From.empty())
return true;
for (const auto *Declaration : Scheduled)
for (const auto &StepName : Declaration->Steps)
if (StepName.Name == ToSchedule.From)
return true;
return false;
}
static llvm::Error
sortPipeline(llvm::SmallVector<const BranchDeclaration *, 2> &ToSort) {
llvm::SmallVector<const BranchDeclaration *> Sorted;
const auto RequirementsMet = [&](const BranchDeclaration *Declaration) {
if (not requirementsMet(Sorted, *Declaration))
return false;
Sorted.push_back(Declaration);
return true;
};
while (not ToSort.empty()) {
size_t InitialSize = ToSort.size();
llvm::erase_if(ToSort, RequirementsMet);
if (InitialSize == ToSort.size())
return revng::createError("Could not satisfy all requirements");
}
ToSort = std::move(Sorted);
return llvm::Error::success();
}
llvm::Expected<Runner>
Loader::load(llvm::ArrayRef<PipelineDeclaration> Pipelines) const {
Runner ToReturn(*PipelineContext);
llvm::SmallVector<const BranchDeclaration *, 2> ToSort;
std::map<const BranchDeclaration *, llvm::StringRef> BranchToComponent;
for (const auto &Pipeline : Pipelines) {
for (const auto &Declaration : Pipeline.Branches) {
ToSort.push_back(&Declaration);
BranchToComponent[&Declaration] = Pipeline.Component;
}
}
if (auto Error = sortPipeline(ToSort))
return std::move(Error);
llvm::StringMap<std::string> ReadOnlyNames;
for (const auto &Declaration : Pipelines)
if (auto Error = parseDeclarations(ToReturn, Declaration, ReadOnlyNames);
Error)
return std::move(Error);
ToReturn.emplaceStep("", "begin", "");
for (const auto *Declaration : ToSort) {
llvm::StringRef Component = BranchToComponent[Declaration];
if (auto Error = parseSteps(ToReturn,
*Declaration,
ReadOnlyNames,
Component);
Error)
return std::move(Error);
}
for (const auto &Declaration : Pipelines)
for (const auto &Analysis : Declaration.Analyses) {
auto MaybeAnalysis = parseAnalysis(Analysis);
if (not MaybeAnalysis)
return MaybeAnalysis.takeError();
ToReturn.getStep(Analysis.Step)
.addAnalysis(Analysis.Name, std::move(*MaybeAnalysis));
}
for (const auto &Declaration : Pipelines)
for (const auto &List : Declaration.AnalysesLists) {
llvm::SmallVector<AnalysisReference, 2> Analyses;
for (auto &AnalysisName : List.UsedAnalysesNames) {
if (not ToReturn.containsAnalysis(AnalysisName)) {
return revng::createError("No known analysis " + AnalysisName
+ " used in analysis list " + List.Name);
}
for (const auto &Step : ToReturn) {
if (Step.hasAnalysis(AnalysisName)) {
Analyses.emplace_back(Step.getName().str(), AnalysisName);
break;
}
}
}
ToReturn.addAnalysesList(List.Name, Analyses);
}
return ToReturn;
}
llvm::Expected<Runner>
Loader::load(const PipelineDeclaration &Declaration) const {
ArrayRef<PipelineDeclaration> Pipelines(&Declaration, 1);
return load(Pipelines);
}
bool Loader::isInvocationUsed(const vector<string> &Invocation) const {
if (Invocation.size() == 0)
return true;
const auto IsStringEnabled = [this](const std::string &Name) {
if (not Name.starts_with("~"))
return EnabledFlags.contains(Name);
string ActualName(Name.begin() + 1, Name.end());
return !EnabledFlags.contains(ActualName);
};
return llvm::any_of(Invocation, IsStringEnabled);
}