Home | History | Annotate | Line # | Download | only in IR
      1 //===- PassManager internal APIs and implementation details -----*- C++ -*-===//
      2 //
      3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
      4 // See https://llvm.org/LICENSE.txt for license information.
      5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
      6 //
      7 //===----------------------------------------------------------------------===//
      8 /// \file
      9 ///
     10 /// This header provides internal APIs and implementation details used by the
     11 /// pass management interfaces exposed in PassManager.h. To understand more
     12 /// context of why these particular interfaces are needed, see that header
     13 /// file. None of these APIs should be used elsewhere.
     14 ///
     15 //===----------------------------------------------------------------------===//
     16 
     17 #ifndef LLVM_IR_PASSMANAGERINTERNAL_H
     18 #define LLVM_IR_PASSMANAGERINTERNAL_H
     19 
     20 #include "llvm/ADT/STLExtras.h"
     21 #include "llvm/ADT/StringRef.h"
     22 #include <memory>
     23 #include <utility>
     24 
     25 namespace llvm {
     26 
     27 template <typename IRUnitT> class AllAnalysesOn;
     28 template <typename IRUnitT, typename... ExtraArgTs> class AnalysisManager;
     29 class PreservedAnalyses;
     30 
     31 // Implementation details of the pass manager interfaces.
     32 namespace detail {
     33 
     34 /// Template for the abstract base class used to dispatch
     35 /// polymorphically over pass objects.
     36 template <typename IRUnitT, typename AnalysisManagerT, typename... ExtraArgTs>
     37 struct PassConcept {
     38   // Boiler plate necessary for the container of derived classes.
     39   virtual ~PassConcept() = default;
     40 
     41   /// The polymorphic API which runs the pass over a given IR entity.
     42   ///
     43   /// Note that actual pass object can omit the analysis manager argument if
     44   /// desired. Also that the analysis manager may be null if there is no
     45   /// analysis manager in the pass pipeline.
     46   virtual PreservedAnalyses run(IRUnitT &IR, AnalysisManagerT &AM,
     47                                 ExtraArgTs... ExtraArgs) = 0;
     48 
     49   /// Polymorphic method to access the name of a pass.
     50   virtual StringRef name() const = 0;
     51 
     52   /// Polymorphic method to to let a pass optionally exempted from skipping by
     53   /// PassInstrumentation.
     54   /// To opt-in, pass should implement `static bool isRequired()`. It's no-op
     55   /// to have `isRequired` always return false since that is the default.
     56   virtual bool isRequired() const = 0;
     57 };
     58 
     59 /// A template wrapper used to implement the polymorphic API.
     60 ///
     61 /// Can be instantiated for any object which provides a \c run method accepting
     62 /// an \c IRUnitT& and an \c AnalysisManager<IRUnit>&. It requires the pass to
     63 /// be a copyable object.
     64 template <typename IRUnitT, typename PassT, typename PreservedAnalysesT,
     65           typename AnalysisManagerT, typename... ExtraArgTs>
     66 struct PassModel : PassConcept<IRUnitT, AnalysisManagerT, ExtraArgTs...> {
     67   explicit PassModel(PassT Pass) : Pass(std::move(Pass)) {}
     68   // We have to explicitly define all the special member functions because MSVC
     69   // refuses to generate them.
     70   PassModel(const PassModel &Arg) : Pass(Arg.Pass) {}
     71   PassModel(PassModel &&Arg) : Pass(std::move(Arg.Pass)) {}
     72 
     73   friend void swap(PassModel &LHS, PassModel &RHS) {
     74     using std::swap;
     75     swap(LHS.Pass, RHS.Pass);
     76   }
     77 
     78   PassModel &operator=(PassModel RHS) {
     79     swap(*this, RHS);
     80     return *this;
     81   }
     82 
     83   PreservedAnalysesT run(IRUnitT &IR, AnalysisManagerT &AM,
     84                          ExtraArgTs... ExtraArgs) override {
     85     return Pass.run(IR, AM, ExtraArgs...);
     86   }
     87 
     88   StringRef name() const override { return PassT::name(); }
     89 
     90   template <typename T>
     91   using has_required_t = decltype(std::declval<T &>().isRequired());
     92 
     93   template <typename T>
     94   static std::enable_if_t<is_detected<has_required_t, T>::value, bool>
     95   passIsRequiredImpl() {
     96     return T::isRequired();
     97   }
     98   template <typename T>
     99   static std::enable_if_t<!is_detected<has_required_t, T>::value, bool>
    100   passIsRequiredImpl() {
    101     return false;
    102   }
    103 
    104   bool isRequired() const override { return passIsRequiredImpl<PassT>(); }
    105 
    106   PassT Pass;
    107 };
    108 
    109 /// Abstract concept of an analysis result.
    110 ///
    111 /// This concept is parameterized over the IR unit that this result pertains
    112 /// to.
    113 template <typename IRUnitT, typename PreservedAnalysesT, typename InvalidatorT>
    114 struct AnalysisResultConcept {
    115   virtual ~AnalysisResultConcept() = default;
    116 
    117   /// Method to try and mark a result as invalid.
    118   ///
    119   /// When the outer analysis manager detects a change in some underlying
    120   /// unit of the IR, it will call this method on all of the results cached.
    121   ///
    122   /// \p PA is a set of preserved analyses which can be used to avoid
    123   /// invalidation because the pass which changed the underlying IR took care
    124   /// to update or preserve the analysis result in some way.
    125   ///
    126   /// \p Inv is typically a \c AnalysisManager::Invalidator object that can be
    127   /// used by a particular analysis result to discover if other analyses
    128   /// results are also invalidated in the event that this result depends on
    129   /// them. See the documentation in the \c AnalysisManager for more details.
    130   ///
    131   /// \returns true if the result is indeed invalid (the default).
    132   virtual bool invalidate(IRUnitT &IR, const PreservedAnalysesT &PA,
    133                           InvalidatorT &Inv) = 0;
    134 };
    135 
    136 /// SFINAE metafunction for computing whether \c ResultT provides an
    137 /// \c invalidate member function.
    138 template <typename IRUnitT, typename ResultT> class ResultHasInvalidateMethod {
    139   using EnabledType = char;
    140   struct DisabledType {
    141     char a, b;
    142   };
    143 
    144   // Purely to help out MSVC which fails to disable the below specialization,
    145   // explicitly enable using the result type's invalidate routine if we can
    146   // successfully call that routine.
    147   template <typename T> struct Nonce { using Type = EnabledType; };
    148   template <typename T>
    149   static typename Nonce<decltype(std::declval<T>().invalidate(
    150       std::declval<IRUnitT &>(), std::declval<PreservedAnalyses>()))>::Type
    151       check(rank<2>);
    152 
    153   // First we define an overload that can only be taken if there is no
    154   // invalidate member. We do this by taking the address of an invalidate
    155   // member in an adjacent base class of a derived class. This would be
    156   // ambiguous if there were an invalidate member in the result type.
    157   template <typename T, typename U> static DisabledType NonceFunction(T U::*);
    158   struct CheckerBase { int invalidate; };
    159   template <typename T> struct Checker : CheckerBase, T {};
    160   template <typename T>
    161   static decltype(NonceFunction(&Checker<T>::invalidate)) check(rank<1>);
    162 
    163   // Now we have the fallback that will only be reached when there is an
    164   // invalidate member, and enables the trait.
    165   template <typename T>
    166   static EnabledType check(rank<0>);
    167 
    168 public:
    169   enum { Value = sizeof(check<ResultT>(rank<2>())) == sizeof(EnabledType) };
    170 };
    171 
    172 /// Wrapper to model the analysis result concept.
    173 ///
    174 /// By default, this will implement the invalidate method with a trivial
    175 /// implementation so that the actual analysis result doesn't need to provide
    176 /// an invalidation handler. It is only selected when the invalidation handler
    177 /// is not part of the ResultT's interface.
    178 template <typename IRUnitT, typename PassT, typename ResultT,
    179           typename PreservedAnalysesT, typename InvalidatorT,
    180           bool HasInvalidateHandler =
    181               ResultHasInvalidateMethod<IRUnitT, ResultT>::Value>
    182 struct AnalysisResultModel;
    183 
    184 /// Specialization of \c AnalysisResultModel which provides the default
    185 /// invalidate functionality.
    186 template <typename IRUnitT, typename PassT, typename ResultT,
    187           typename PreservedAnalysesT, typename InvalidatorT>
    188 struct AnalysisResultModel<IRUnitT, PassT, ResultT, PreservedAnalysesT,
    189                            InvalidatorT, false>
    190     : AnalysisResultConcept<IRUnitT, PreservedAnalysesT, InvalidatorT> {
    191   explicit AnalysisResultModel(ResultT Result) : Result(std::move(Result)) {}
    192   // We have to explicitly define all the special member functions because MSVC
    193   // refuses to generate them.
    194   AnalysisResultModel(const AnalysisResultModel &Arg) : Result(Arg.Result) {}
    195   AnalysisResultModel(AnalysisResultModel &&Arg)
    196       : Result(std::move(Arg.Result)) {}
    197 
    198   friend void swap(AnalysisResultModel &LHS, AnalysisResultModel &RHS) {
    199     using std::swap;
    200     swap(LHS.Result, RHS.Result);
    201   }
    202 
    203   AnalysisResultModel &operator=(AnalysisResultModel RHS) {
    204     swap(*this, RHS);
    205     return *this;
    206   }
    207 
    208   /// The model bases invalidation solely on being in the preserved set.
    209   //
    210   // FIXME: We should actually use two different concepts for analysis results
    211   // rather than two different models, and avoid the indirect function call for
    212   // ones that use the trivial behavior.
    213   bool invalidate(IRUnitT &, const PreservedAnalysesT &PA,
    214                   InvalidatorT &) override {
    215     auto PAC = PA.template getChecker<PassT>();
    216     return !PAC.preserved() &&
    217            !PAC.template preservedSet<AllAnalysesOn<IRUnitT>>();
    218   }
    219 
    220   ResultT Result;
    221 };
    222 
    223 /// Specialization of \c AnalysisResultModel which delegates invalidate
    224 /// handling to \c ResultT.
    225 template <typename IRUnitT, typename PassT, typename ResultT,
    226           typename PreservedAnalysesT, typename InvalidatorT>
    227 struct AnalysisResultModel<IRUnitT, PassT, ResultT, PreservedAnalysesT,
    228                            InvalidatorT, true>
    229     : AnalysisResultConcept<IRUnitT, PreservedAnalysesT, InvalidatorT> {
    230   explicit AnalysisResultModel(ResultT Result) : Result(std::move(Result)) {}
    231   // We have to explicitly define all the special member functions because MSVC
    232   // refuses to generate them.
    233   AnalysisResultModel(const AnalysisResultModel &Arg) : Result(Arg.Result) {}
    234   AnalysisResultModel(AnalysisResultModel &&Arg)
    235       : Result(std::move(Arg.Result)) {}
    236 
    237   friend void swap(AnalysisResultModel &LHS, AnalysisResultModel &RHS) {
    238     using std::swap;
    239     swap(LHS.Result, RHS.Result);
    240   }
    241 
    242   AnalysisResultModel &operator=(AnalysisResultModel RHS) {
    243     swap(*this, RHS);
    244     return *this;
    245   }
    246 
    247   /// The model delegates to the \c ResultT method.
    248   bool invalidate(IRUnitT &IR, const PreservedAnalysesT &PA,
    249                   InvalidatorT &Inv) override {
    250     return Result.invalidate(IR, PA, Inv);
    251   }
    252 
    253   ResultT Result;
    254 };
    255 
    256 /// Abstract concept of an analysis pass.
    257 ///
    258 /// This concept is parameterized over the IR unit that it can run over and
    259 /// produce an analysis result.
    260 template <typename IRUnitT, typename PreservedAnalysesT, typename InvalidatorT,
    261           typename... ExtraArgTs>
    262 struct AnalysisPassConcept {
    263   virtual ~AnalysisPassConcept() = default;
    264 
    265   /// Method to run this analysis over a unit of IR.
    266   /// \returns A unique_ptr to the analysis result object to be queried by
    267   /// users.
    268   virtual std::unique_ptr<
    269       AnalysisResultConcept<IRUnitT, PreservedAnalysesT, InvalidatorT>>
    270   run(IRUnitT &IR, AnalysisManager<IRUnitT, ExtraArgTs...> &AM,
    271       ExtraArgTs... ExtraArgs) = 0;
    272 
    273   /// Polymorphic method to access the name of a pass.
    274   virtual StringRef name() const = 0;
    275 };
    276 
    277 /// Wrapper to model the analysis pass concept.
    278 ///
    279 /// Can wrap any type which implements a suitable \c run method. The method
    280 /// must accept an \c IRUnitT& and an \c AnalysisManager<IRUnitT>& as arguments
    281 /// and produce an object which can be wrapped in a \c AnalysisResultModel.
    282 template <typename IRUnitT, typename PassT, typename PreservedAnalysesT,
    283           typename InvalidatorT, typename... ExtraArgTs>
    284 struct AnalysisPassModel : AnalysisPassConcept<IRUnitT, PreservedAnalysesT,
    285                                                InvalidatorT, ExtraArgTs...> {
    286   explicit AnalysisPassModel(PassT Pass) : Pass(std::move(Pass)) {}
    287   // We have to explicitly define all the special member functions because MSVC
    288   // refuses to generate them.
    289   AnalysisPassModel(const AnalysisPassModel &Arg) : Pass(Arg.Pass) {}
    290   AnalysisPassModel(AnalysisPassModel &&Arg) : Pass(std::move(Arg.Pass)) {}
    291 
    292   friend void swap(AnalysisPassModel &LHS, AnalysisPassModel &RHS) {
    293     using std::swap;
    294     swap(LHS.Pass, RHS.Pass);
    295   }
    296 
    297   AnalysisPassModel &operator=(AnalysisPassModel RHS) {
    298     swap(*this, RHS);
    299     return *this;
    300   }
    301 
    302   // FIXME: Replace PassT::Result with type traits when we use C++11.
    303   using ResultModelT =
    304       AnalysisResultModel<IRUnitT, PassT, typename PassT::Result,
    305                           PreservedAnalysesT, InvalidatorT>;
    306 
    307   /// The model delegates to the \c PassT::run method.
    308   ///
    309   /// The return is wrapped in an \c AnalysisResultModel.
    310   std::unique_ptr<
    311       AnalysisResultConcept<IRUnitT, PreservedAnalysesT, InvalidatorT>>
    312   run(IRUnitT &IR, AnalysisManager<IRUnitT, ExtraArgTs...> &AM,
    313       ExtraArgTs... ExtraArgs) override {
    314     return std::make_unique<ResultModelT>(
    315         Pass.run(IR, AM, std::forward<ExtraArgTs>(ExtraArgs)...));
    316   }
    317 
    318   /// The model delegates to a static \c PassT::name method.
    319   ///
    320   /// The returned string ref must point to constant immutable data!
    321   StringRef name() const override { return PassT::name(); }
    322 
    323   PassT Pass;
    324 };
    325 
    326 } // end namespace detail
    327 
    328 } // end namespace llvm
    329 
    330 #endif // LLVM_IR_PASSMANAGERINTERNAL_H
    331