#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/Support/Debug.h" namespace pipeline { template concept IsContext = std::is_convertible_v; template concept IsConstContext = std::is_convertible_v; template concept IsContainer = std::is_base_of_v>; template concept HasName = requires() { { T::Name } -> convertible_to; }; template concept HasContract = requires(T P) { { llvm::ArrayRef(P.getContract()) }; }; /// A Pipe is a class with the following characteristics: /// /// * It must have a static constexpr field named Name that is a string /// describing its name. Mostly used for debug purposes. /// * A std::array getContract() const method that returns /// the contract of such class. /// * a void run(T...) method where the first argument must be a Context and /// every other type must be the most derived type of a Container. /// The operation performed by run MUST be consisted with the contract. template concept Pipe = HasName and HasContract and(IsContainer and...) and (IsContext || IsConstContext); namespace detail { using StringArrayRef = llvm::ArrayRef; template auto &getContainerFromName(ContainerSet &Containers, llvm::StringRef Name) { return llvm::cast(Containers[Name]); } template void invokeImpl(Context &Ctx, PipeType &Pipe, void (PipeType::*F)(Context &, Args...), ContainerSet &Containers, const StringArrayRef &ArgsNames, const std::integer_sequence &) { using namespace std; (Pipe.*F)(Ctx, getContainerFromName>(Containers, ArgsNames[S])...); } template void invokeImpl(const Context &Ctx, PipeType &Pipe, void (PipeType::*F)(const Context &, Args...), ContainerSet &Containers, const StringArrayRef &ArgsNames, const std::integer_sequence &) { using namespace std; (Pipe.*F)(Ctx, getContainerFromName>(Containers, ArgsNames[S])...); } /// Invokes the F member function on the Pipe Pipe passing as nth argument the /// container with the name equal to the nth element of ArgsNames. template void invokePipeFunction(Context &Ctx, PipeType &Pipe, void (PipeType::*F)(Context &, Args...), ContainerSet &Containers, const llvm::ArrayRef &ArgsNames) { constexpr auto Indexes = std::make_integer_sequence(); revng_assert(sizeof...(Args) == ArgsNames.size()); invokeImpl(Ctx, Pipe, F, Containers, ArgsNames, Indexes); } template void invokePipeFunction(Context &Ctx, PipeType &Pipe, void (PipeType::*F)(const Context &, Args...), ContainerSet &Containers, const llvm::ArrayRef &ArgsNames) { constexpr auto Indexes = std::make_integer_sequence(); revng_assert(sizeof...(Args) == ArgsNames.size()); invokeImpl(Ctx, Pipe, F, Containers, ArgsNames, Indexes); } template constexpr bool checkPipe(void (C::*)(First, Rest...)) requires Pipe { return true; } class PipeWrapperBase { public: virtual void run(Context &Ctx, ContainerSet &Containers) = 0; virtual ContainerToTargetsMap getRequirements(const ContainerToTargetsMap &Target) const = 0; virtual ContainerToTargetsMap deduceResults(ContainerToTargetsMap &Target) const = 0; virtual ~PipeWrapperBase() = default; virtual std::unique_ptr clone() const = 0; virtual std::vector getRunningContainersNames() const = 0; virtual std::string getName() const = 0; virtual void dump(std::ostream &OS, size_t Indents) const = 0; virtual void print(const Context &Ctx, llvm::raw_ostream &OS, size_t Indents) const = 0; virtual bool areRequirementsMet(const ContainerToTargetsMap &Input) const = 0; }; template concept Dumpable = requires(T D) { { D.dump(dbg, 0) }; }; template concept Printable = requires(PipeType Pipe) { { Pipe.print(std::declval(), llvm::outs(), std::declval>()) }; }; /// 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); private: PipeType ActualPipe; std::vector RunningContainersNames; public: PipeWrapperImpl(PipeType ActualPipe, std::vector RunningContainersNames) : ActualPipe(std::move(ActualPipe)), RunningContainersNames(std::move(RunningContainersNames)) {} ~PipeWrapperImpl() override = default; public: std::string getName() const override { return PipeType::Name; } public: void run(Context &Ctx, ContainerSet &Containers) override { invokePipeFunction(Ctx, ActualPipe, &PipeType::run, Containers, RunningContainersNames); } public: bool areRequirementsMet(const ContainerToTargetsMap &Input) const override { const auto &Contracts = ActualPipe.getContract(); if (Contracts.size() == 0) return true; ContainerToTargetsMap ToCheck = Input; for (const auto &Contract : Contracts) { if (Contract.forwardMatches(ToCheck, RunningContainersNames)) return true; Contract.deduceResults(ToCheck, RunningContainersNames); } return false; } ContainerToTargetsMap getRequirements(const ContainerToTargetsMap &Target) const override { const auto &Contracts = ActualPipe.getContract(); auto ToReturn = Target; for (const auto &Contract : llvm::reverse(Contracts)) ToReturn = Contract.deduceRequirements(ToReturn, RunningContainersNames); return ToReturn; } ContainerToTargetsMap deduceResults(ContainerToTargetsMap &Target) const override { const auto &Contracts = ActualPipe.getContract(); for (const auto &Contract : Contracts) Contract.deduceResults(Target, RunningContainersNames); return Target; } std::unique_ptr clone() const override { return std::make_unique(*this); } std::vector getRunningContainersNames() const override { return RunningContainersNames; } public: void dump(std::ostream &OS, size_t Indentation) const override { indent(OS, Indentation); OS << getName() << "\n"; indent(OS, Indentation + 1); OS << "Containers\n"; for (const auto &Name : getRunningContainersNames()) { indent(OS, Indentation + 2); OS << Name; OS << "\n"; } if constexpr (Dumpable) ActualPipe.dump(OS, Indentation); } void print(const Context &Ctx, llvm::raw_ostream &OS, size_t Indentation) const override { if constexpr (Printable) { indent(OS, Indentation); const auto &Names = getRunningContainersNames(); ActualPipe.print(Ctx, OS, Names); } } }; } // namespace detail /// This class is used to hide the unique ptr and expose a concrete class /// instead of pointers, as well as implementing dump and operator=, which /// is implemented as a clone. class PipeWrapper { private: std::unique_ptr Pipe; public: template PipeWrapper(PipeType Pipe, std::vector RunningContainersNames) : Pipe(makeUniqueWrapper(Pipe, std::move(RunningContainersNames))) {} PipeWrapper(const PipeWrapper &Other) : Pipe(Other.Pipe->clone()) {} PipeWrapper(PipeWrapper &&Other) = default; PipeWrapper &operator=(const PipeWrapper &Other); PipeWrapper &operator=(PipeWrapper &&Other) = default; ~PipeWrapper() = default; public: template static PipeWrapper makeWrapper(std::vector RunningContainersNames) { return PipeWrapper(PipeType(), std::move(RunningContainersNames)); } public: detail::PipeWrapperBase &operator*() { return *Pipe; } const detail::PipeWrapperBase &operator*() const { return *Pipe; } detail::PipeWrapperBase *operator->() { return Pipe.get(); } const detail::PipeWrapperBase *operator->() const { return Pipe.get(); } public: template void dump(OStream &OS, size_t Indentation = 0) const { Pipe->dump(OS, Indentation + 1); } void dump() const { dump(dbg); } private: template auto makeUniqueWrapper(PipeType Pipe, std::vector RunningContainersNames) { using Wrapper = detail::PipeWrapperImpl; return std::make_unique(Pipe, std::move(RunningContainersNames)); } }; template PipeWrapper bindPipe(ContainerNames &&...Names) { auto NamesList = { std::forward(Names)... }; return PipeWrapper::makeWrapper(std::move(NamesList)); } template PipeWrapper bindPipe(PipeType &&E, ContainerNames &&...Names) { auto NamesList = { std::forward(Names)... }; return PipeWrapper(std::forward(E), std::move(NamesList)); } } // namespace pipeline