#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include #include #include #include #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/iterator_range.h" #include "llvm/Support/Error.h" #include "revng/ADT/STLExtras.h" #include "revng/Pipeline/Container.h" #include "revng/Pipeline/ContainerSet.h" #include "revng/Pipeline/Context.h" #include "revng/Pipeline/Contract.h" #include "revng/Pipeline/Invokable.h" #include "revng/Pipeline/Target.h" #include "revng/Support/Debug.h" namespace pipeline { namespace detail { template concept HasContract = requires(T P) { { llvm::ArrayRef(P.getContract()) }; }; template concept Pipe = Invokable and (IsContainer and ...) and HasContract; template concept HasPrecondition = requires(const T &P) { { P.checkPrecondition }; }; template constexpr bool checkPipe(auto (C::*)(First, Rest...)) requires Pipe { return true; } template class PipeWrapperImpl; class PipeWrapperBase : public InvokableWrapperBase { public: template using ImplType = PipeWrapperImpl; public: virtual ContainerToTargetsMap getRequirements(const Context &Ctx, const ContainerToTargetsMap &Target) const = 0; virtual ContainerToTargetsMap deduceResults(const Context &Ctx, ContainerToTargetsMap &Target) const = 0; virtual bool areRequirementsMet(const Context &Ctx, const ContainerToTargetsMap &Input) const = 0; virtual std::unique_ptr clone(std::vector NewRunningContainersNames = {}) const = 0; virtual llvm::Error checkPrecondition(const Context &Ctx) const = 0; virtual ~PipeWrapperBase() = default; }; /// A pipe must be type erased somehow to become compatible with a pipeline, /// a PipeWrapperImpl takes care of this issue, it can be constructed from /// any pipeline type, and it will expose the contract and run method of that /// enforcer. template class PipeWrapperImpl : public PipeWrapperBase { private: static constexpr bool CheckPipe = checkPipe(&PipeType::run); static_assert(CheckPipe); InvokableWrapperImpl Invokable; public: PipeWrapperImpl(PipeType ActualPipe, std::vector RunningContainersNames) : Invokable(std::move(ActualPipe), std::move(RunningContainersNames)) {} PipeWrapperImpl(const PipeWrapperImpl &ActualPipe, std::vector RunningContainersNames) : Invokable(ActualPipe.Invokable, std::move(RunningContainersNames)) {} PipeWrapperImpl(PipeWrapperImpl &&ActualPipe, std::vector RunningContainersNames) : Invokable(std::move(ActualPipe.Invokable), std::move(RunningContainersNames)) {} public: bool areRequirementsMet(const Context &Ctx, const ContainerToTargetsMap &Input) const override { const auto &Contracts = Invokable.getPipe().getContract(); if (Contracts.size() == 0) return true; ContainerToTargetsMap ToCheck = Input; for (const auto &Contract : Contracts) { if (Contract.forwardMatches(Ctx, ToCheck, Invokable.getRunningContainersNames())) return true; Contract.deduceResults(Ctx, ToCheck, Invokable.getRunningContainersNames()); } return false; } ContainerToTargetsMap getRequirements(const Context &Ctx, const ContainerToTargetsMap &Target) const override { const auto &Contracts = Invokable.getPipe().getContract(); auto ToReturn = Target; for (const auto &Contract : llvm::reverse(Contracts)) ToReturn = Contract .deduceRequirements(Ctx, ToReturn, Invokable.getRunningContainersNames()); return ToReturn; } ContainerToTargetsMap deduceResults(const Context &Ctx, ContainerToTargetsMap &Target) const override { const auto &Contracts = Invokable.getPipe().getContract(); for (const auto &Contract : Contracts) Contract.deduceResults(Ctx, Target, Invokable.getRunningContainersNames()); return Target; } std::unique_ptr clone(std::vector NewContainersNames = {}) const override { if (NewContainersNames.empty()) return std::make_unique(*this); return std::make_unique(*this, std::move(NewContainersNames)); } llvm::Error checkPrecondition(const Context &Ctx) const override { if constexpr (not HasPrecondition) return llvm::Error::success(); else return Invokable.getPipe().checkPrecondition(Ctx); } public: void dump(std::ostream &OS, size_t Indentation) const override debug_function { Invokable.dump(OS, Indentation); } void print(const Context &Ctx, llvm::raw_ostream &OS, size_t Indentation) const override { Invokable.print(Ctx, OS, Indentation); } llvm::Error run(Context &Ctx, ContainerSet &Containers, const llvm::StringMap &ExtraArgs) override { return Invokable.run(Ctx, Containers, ExtraArgs); } std::vector getOptionsNames() const override { return Invokable.getOptionsNames(); } std::vector getOptionsTypes() const override { return Invokable.getOptionsTypes(); } std::vector getRunningContainersNames() const override { return Invokable.getRunningContainersNames(); } bool isContainerArgumentConst(size_t ArgumentIndex) const override { return Invokable.isContainerArgumentConst(ArgumentIndex); } std::string getName() const override { return Invokable.getName(); } }; } // namespace detail using PipeWrapper = InvokableWrapper; } // namespace pipeline