[polly] r280948 - Add -polly-flatten-schedule pass.

Tobias Grosser via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 9 05:52:29 PDT 2016


Hi Michael,

I see the following error in my unittests:

Value of: checkFlatten( "[n] -> { A[i] -> [0, i] : 0 <= i < n; B[i] ->
[1, i] : 0 <= i < n }", "[n] -> { A[i] -> [i] : 0 <= i < n; B[i] -> [n +
i] : 0 <= i < n }")
  Actual: false
Expected: true
[  FAILED  ] Flatten.FlattenSequence (5 ms)
[----------] 1 test from Flatten (5 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (5 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] Flatten.FlattenSequence

 1 FAILED TEST
/home/grosser/Projects/polly/git/tools/polly/lib/External/isl/isl_space.c:1775:
not a parameter space
/home/grosser/Projects/polly/git/tools/polly/lib/External/isl/isl_space.c:1775:
not a parameter space
/home/grosser/Projects/polly/git/tools/polly/lib/External/isl/isl_space.c:1775:
not a parameter space
/home/grosser/Projects/polly/git/tools/polly/lib/External/isl/isl_space.c:1775:
not a parameter space
/home/grosser/Projects/polly/git/tools/polly/lib/External/isl/isl_space.c:1775:
not a parameter space
/home/grosser/Projects/polly/git/tools/polly/lib/External/isl/isl_space.c:1775:
not a parameter space
/home/grosser/Projects/polly/git/tools/polly/lib/External/isl/isl_space.c:1775:
not a parameter space
/home/grosser/Projects/polly/git/tools/polly/lib/External/isl/isl_space.c:1775:
not a parameter space
/home/grosser/Projects/polly/git/tools/polly/lib/External/isl/isl_space.c:1775:
not a parameter space

Best,
Tobias

On Thu, Sep 8, 2016, at 05:02 PM, Michael Kruse via llvm-commits wrote:
> Author: meinersbur
> Date: Thu Sep  8 10:02:36 2016
> New Revision: 280948
> 
> URL: http://llvm.org/viewvc/llvm-project?rev=280948&view=rev
> Log:
> Add -polly-flatten-schedule pass.
> 
> The -polly-flatten-schedule pass reduces the number of scattering
> dimensions in its isl_union_map form to make them easier to understand.
> It is not meant to be used in production, only for debugging and
> regression tests.
> 
> To illustrate, how it can make sets simpler, here is a lifetime set
> used computed by the porposed DeLICM pass without flattening:
> 
>     { Stmt_reduction_for[0, 4] -> [0, 2, o2, o3] : o2 < 0;
>       Stmt_reduction_for[0, 4] -> [0, 1, o2, o3] : o2 >= 5;
>       Stmt_reduction_for[0, 4] -> [0, 1, 4, o3] : o3 > 0;
>       Stmt_reduction_for[0, i1] -> [0, 1, i1, 1] : 0 <= i1 <= 3;
>       Stmt_reduction_for[0, 4] -> [0, 2, 0, o3] : o3 <= 0 }
> 
> And here the same lifetime for a semantically identical one-dimensional
> schedule:
> 
>     { Stmt_reduction_for[0, i1] -> [2 + 3i1] : 0 <= i1 <= 4 }
> 
> Differential Revision: https://reviews.llvm.org/D24310
> 
> Added:
>     polly/trunk/include/polly/FlattenAlgo.h
>     polly/trunk/include/polly/FlattenSchedule.h
>     polly/trunk/lib/Transform/FlattenAlgo.cpp
>     polly/trunk/lib/Transform/FlattenSchedule.cpp
>     polly/trunk/test/FlattenSchedule/
>     polly/trunk/test/FlattenSchedule/gemm.ll
>     polly/trunk/unittests/Flatten/
>     polly/trunk/unittests/Flatten/CMakeLists.txt
>     polly/trunk/unittests/Flatten/FlattenTest.cpp
> Modified:
>     polly/trunk/include/polly/LinkAllPasses.h
>     polly/trunk/include/polly/Support/GICHelper.h
>     polly/trunk/lib/CMakeLists.txt
>     polly/trunk/lib/Support/GICHelper.cpp
>     polly/trunk/lib/Support/RegisterPasses.cpp
>     polly/trunk/test/update_check.py
>     polly/trunk/unittests/CMakeLists.txt
> 
> Added: polly/trunk/include/polly/FlattenAlgo.h
> URL:
> http://llvm.org/viewvc/llvm-project/polly/trunk/include/polly/FlattenAlgo.h?rev=280948&view=auto
> ==============================================================================
> --- polly/trunk/include/polly/FlattenAlgo.h (added)
> +++ polly/trunk/include/polly/FlattenAlgo.h Thu Sep  8 10:02:36 2016
> @@ -0,0 +1,38 @@
> +//===------ FlattenAlgo.h --------------------------------------*- C++
> -*-===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// Main algorithm of the FlattenSchedulePass. This is a separate file to
> avoid
> +// the unittest for this requiring linking against LLVM.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef POLLY_FLATTENALGO_H
> +#define POLLY_FLATTENALGO_H
> +
> +#include "polly/Support/GICHelper.h"
> +
> +namespace polly {
> +/// Recursively flatten a schedule.
> +///
> +/// Reduce the number of scatter dimensions as much as possible without
> changing
> +/// the relative order of instances in a schedule. Ideally, this results
> in a
> +/// single scatter dimension, but it may not always be possible to
> combine
> +/// dimensions, eg. if a dimension is unbounded. In worst case, the
> original
> +/// schedule is returned.
> +///
> +/// Schedules with fewer dimensions may be easier to understand for
> humans, but
> +/// it should make no difference to the computer.
> +///
> +/// @param Schedule The input schedule.
> +///
> +/// @return The flattened schedule.
> +IslPtr<isl_union_map> flattenSchedule(IslPtr<isl_union_map> Schedule);
> +} // namespace polly
> +
> +#endif /* POLLY_FLATTENALGO_H */
> 
> Added: polly/trunk/include/polly/FlattenSchedule.h
> URL:
> http://llvm.org/viewvc/llvm-project/polly/trunk/include/polly/FlattenSchedule.h?rev=280948&view=auto
> ==============================================================================
> --- polly/trunk/include/polly/FlattenSchedule.h (added)
> +++ polly/trunk/include/polly/FlattenSchedule.h Thu Sep  8 10:02:36 2016
> @@ -0,0 +1,32 @@
> +//===------ FlattenSchedule.h ----------------------------------*- C++
> -*-===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// Try to reduce the number of scatter dimension. Useful to make
> isl_union_map
> +// schedules more understandable. This is only intended for debugging
> and
> +// unittests, not for optimizations themselves.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef POLLY_FLATTENSCHEDULE_H
> +#define POLLY_FLATTENSCHEDULE_H
> +
> +namespace llvm {
> +class PassRegistry;
> +class Pass;
> +} // anonymous namespace
> +
> +namespace polly {
> +llvm::Pass *createFlattenSchedulePass();
> +} // namespace polly
> +
> +namespace llvm {
> +void initializeFlattenSchedulePass(llvm::PassRegistry &);
> +} // namespace llvm
> +
> +#endif /* POLLY_FLATTENSCHEDULE_H */
> 
> Modified: polly/trunk/include/polly/LinkAllPasses.h
> URL:
> http://llvm.org/viewvc/llvm-project/polly/trunk/include/polly/LinkAllPasses.h?rev=280948&r1=280947&r2=280948&view=diff
> ==============================================================================
> --- polly/trunk/include/polly/LinkAllPasses.h (original)
> +++ polly/trunk/include/polly/LinkAllPasses.h Thu Sep  8 10:02:36 2016
> @@ -47,6 +47,7 @@ llvm::Pass *createCodeGenerationPass();
>  llvm::Pass *createPPCGCodeGenerationPass();
>  #endif
>  llvm::Pass *createIslScheduleOptimizerPass();
> +llvm::Pass *createFlattenSchedulePass();
>  
>  extern char &CodePreparationID;
>  } // namespace polly
> @@ -80,6 +81,7 @@ struct PollyForcePassLinking {
>      polly::createPPCGCodeGenerationPass();
>  #endif
>      polly::createIslScheduleOptimizerPass();
> +    polly::createFlattenSchedulePass();
>    }
>  } PollyForcePassLinking; // Force link by creating a global definition.
>  } // namespace
> @@ -97,6 +99,7 @@ void initializePPCGCodeGenerationPass(ll
>  #endif
>  void initializeIslScheduleOptimizerPass(llvm::PassRegistry &);
>  void initializePollyCanonicalizePass(llvm::PassRegistry &);
> +void initializeFlattenSchedulePass(llvm::PassRegistry &);
>  } // namespace llvm
>  
>  #endif
> 
> Modified: polly/trunk/include/polly/Support/GICHelper.h
> URL:
> http://llvm.org/viewvc/llvm-project/polly/trunk/include/polly/Support/GICHelper.h?rev=280948&r1=280947&r2=280948&view=diff
> ==============================================================================
> --- polly/trunk/include/polly/Support/GICHelper.h (original)
> +++ polly/trunk/include/polly/Support/GICHelper.h Thu Sep  8 10:02:36
> 2016
> @@ -16,21 +16,17 @@
>  
>  #include "llvm/ADT/APInt.h"
>  #include "llvm/Support/raw_ostream.h"
> +#include "isl/aff.h"
>  #include "isl/ctx.h"
> +#include "isl/map.h"
> +#include "isl/set.h"
> +#include "isl/union_map.h"
> +#include "isl/union_set.h"
> +#include <functional>
>  #include <string>
>  
> -struct isl_map;
> -struct isl_union_map;
> -struct isl_set;
> -struct isl_union_set;
>  struct isl_schedule;
>  struct isl_multi_aff;
> -struct isl_pw_multi_aff;
> -struct isl_union_pw_multi_aff;
> -struct isl_aff;
> -struct isl_pw_aff;
> -struct isl_val;
> -struct isl_space;
>  
>  namespace llvm {
>  class Value;
> @@ -177,6 +173,239 @@ std::string getIslCompatibleName(const s
>                                   const std::string &Middle,
>                                   const std::string &Suffix);
>  
> +/// IslObjTraits<isl_*> is a static class to invoke common functions
> that all
> +/// ISL objects have: isl_*_copy, isl_*_free, isl_*_get_ctx and
> isl_*_to_str.
> +/// These functions follow a common naming scheme, but not a base class
> +/// hierarchy (as ISL is written in C). As such, the functions are
> accessible
> +/// only by constructing the function name using the preprocessor. This
> class
> +/// serves to make these names accessible to a C++ template scheme.
> +///
> +/// There is an isl_obj polymorphism layer, but its implementation is
> +/// incomplete.
> +template <typename T> class IslObjTraits;
> +
> +#define DECLARE_TRAITS(TYPE)                                            
>       \
> +  template <> class IslObjTraits<isl_##TYPE> {                          
>       \
> +  public:                                                               
>       \
> +    static __isl_give isl_##TYPE *copy(__isl_keep isl_##TYPE *Obj) {    
>       \
> +      return isl_##TYPE##_copy(Obj);                                    
>       \
> +    }                                                                   
>       \
> +    static void free(__isl_take isl_##TYPE *Obj) {
> isl_##TYPE##_free(Obj); }   \
> +    static isl_ctx *get_ctx(__isl_keep isl_##TYPE *Obj) {               
>       \
> +      return isl_##TYPE##_get_ctx(Obj);                                 
>       \
> +    }                                                                   
>       \
> +    static std::string to_str(__isl_keep isl_##TYPE *Obj) {             
>       \
> +      if (!Obj)                                                         
>       \
> +        return "null";                                                  
>       \
> +      char *cstr = isl_##TYPE##_to_str(Obj);                            
>       \
> +      if (!cstr)                                                        
>       \
> +        return "null";                                                  
>       \
> +      std::string Result{cstr};                                         
>       \
> +      ::free(cstr);                                                     
>       \
> +      return Result;                                                    
>       \
> +    }                                                                   
>       \
> +  };
> +
> +DECLARE_TRAITS(val)
> +DECLARE_TRAITS(space)
> +DECLARE_TRAITS(basic_map)
> +DECLARE_TRAITS(map)
> +DECLARE_TRAITS(union_map)
> +DECLARE_TRAITS(basic_set)
> +DECLARE_TRAITS(set)
> +DECLARE_TRAITS(union_set)
> +DECLARE_TRAITS(aff)
> +DECLARE_TRAITS(pw_aff)
> +DECLARE_TRAITS(union_pw_aff)
> +DECLARE_TRAITS(multi_union_pw_aff)
> +DECLARE_TRAITS(union_pw_multi_aff)
> +
> +template <typename T> class NonowningIslPtr;
> +
> +/// Smart pointer to an ISL object.
> +///
> +/// An object of this class owns an reference of an ISL object, meaning
> if will
> +/// free it when destroyed. Most ISL objects are reference counted such
> that we
> +/// gain an automatic memory management.
> +///
> +/// Function parameters in the ISL API are annotated using either
> __isl_keep
> +/// __isl_take. Return values that are objects are annotated using
> __is_give,
> +/// meaning the caller is responsible for releasing the object. When
> annotated
> +/// with __isl_keep, use the keep() function to pass a plain pointer to
> the ISL
> +/// object. For __isl_take-annotated parameters, use either copy() to
> increase
> +/// the reference counter by one, or take() to pass the ownership to the
> called
> +/// function. When IslPtr loses ownership, it cannot be used anymore and
> won't
> +/// free the object when destroyed. Use the give() function to wrap the
> +/// ownership of a returned isl_* object into an IstPtr<isl_*>.
> +///
> +/// There is purposefully no implicit conversion from/to plain isl_*
> pointers to
> +/// avoid difficult to find bugs because keep/copy/take would have been
> +/// required.
> +template <typename T> class IslPtr {
> +  typedef IslPtr<T> ThisTy;
> +  typedef IslObjTraits<T> Traits;
> +
> +private:
> +  T *Obj;
> +
> +  explicit IslPtr(__isl_take T *Obj, bool TakeOwnership)
> +      : Obj(TakeOwnership ? Obj : Traits::copy(Obj)) {}
> +
> +public:
> +  IslPtr() : Obj(nullptr) {}
> +  /* implicit */ IslPtr(nullptr_t That) : IslPtr() {}
> +
> +  /* implicit */ IslPtr(const ThisTy &That) : IslPtr(That.Obj, false) {}
> +  /* implicit */ IslPtr(ThisTy &&That) : IslPtr(That.Obj, true) {
> +    That.Obj = nullptr;
> +  }
> +  /* implicit */ IslPtr(NonowningIslPtr<T> That) : IslPtr(That.copy(),
> true) {}
> +  ~IslPtr() { Traits::free(Obj); }
> +
> +  ThisTy &operator=(const ThisTy &That) {
> +    Traits::free(this->Obj);
> +    this->Obj = Traits::copy(That.Obj);
> +    return *this;
> +  }
> +  ThisTy &operator=(ThisTy &&That) {
> +    swap(*this, That);
> +    return *this;
> +  }
> +
> +  explicit operator bool() const { return Obj; }
> +
> +  static void swap(ThisTy &LHS, ThisTy &RHS) { std::swap(LHS.Obj,
> RHS.Obj); }
> +
> +  static ThisTy give(T *Obj) { return ThisTy(Obj, true); }
> +  T *keep() const { return Obj; }
> +  T *take() {
> +    auto *Result = Obj;
> +    Obj = nullptr;
> +    return Result;
> +  }
> +  T *copy() const { return Traits::copy(Obj); }
> +
> +  isl_ctx *getCtx() const { return Traits::get_ctx(Obj); }
> +  std::string toStr() const { return Traits::to_str(Obj); }
> +};
> +
> +template <typename T> static IslPtr<T> give(__isl_take T *Obj) {
> +  return IslPtr<T>::give(Obj);
> +}
> +
> +template <typename T>
> +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const IslPtr<T>
> &Obj) {
> +  OS << IslObjTraits<T>::to_str(Obj.keep());
> +  return OS;
> +}
> +
> +/// Smart pointer to an ISL object, but does not release it when
> destroyed.
> +///
> +/// This is meant to be used as function parameter type. The caller
> guarantees
> +/// that the reference is alive during the function's execution and
> hence
> +/// doesn't need to add a reference. Therefore, it is equivalent to the
> +/// __isl_keep annotation (IslPtr being equivalent to __isl_take which
> can be
> +/// either copied or moved).
> +///
> +/// Just as IslPtr, it has keep() and copy() methods. The take() method
> is
> +/// missing as this would steal the reference from the owner (the
> caller).
> +template <typename T> class NonowningIslPtr {
> +  typedef NonowningIslPtr<T> ThisTy;
> +  typedef IslObjTraits<T> Traits;
> +
> +private:
> +  T *Obj;
> +
> +  /* implicit */ NonowningIslPtr(__isl_keep T *Obj) : Obj(Obj) {}
> +
> +public:
> +  NonowningIslPtr() : Obj(nullptr) {}
> +  /* implicit */ NonowningIslPtr(nullptr_t That) : NonowningIslPtr() {}
> +
> +  /* implicit */ NonowningIslPtr(const IslPtr<T> &That)
> +      : NonowningIslPtr(That.keep()) {}
> +
> +  explicit operator bool() const { return Obj; }
> +
> +  static void swap(ThisTy &LHS, ThisTy &RHS) { std::swap(LHS.Obj,
> RHS.Obj); }
> +
> +  T *keep() const { return Obj; }
> +  T *copy() const { return Traits::copy(Obj); }
> +
> +  isl_ctx *getCtx() const { return Traits::get_ctx(Obj); }
> +  std::string toStr() const { return Traits::to_str(Obj); }
> +};
> +
> +template <typename T>
> +static llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
> +                                     NonowningIslPtr<T> Obj) {
> +  OS << IslObjTraits<T>::to_str(Obj.keep());
> +  return OS;
> +}
> +
> +/// Enumerate all isl_maps of an isl_union_map.
> +///
> +/// This basically wraps isl_union_map_foreach_map() and allows to call
> back
> +/// C++11 closures.
> +void foreachElt(NonowningIslPtr<isl_union_map> UMap,
> +                const std::function<void(IslPtr<isl_map> Map)> &F);
> +
> +/// Enumerate all isl_pw_aff of an isl_union_pw_aff.
> +///
> +/// This basically wraps isl_union_pw_aff(), but also allows to call
> back C++11
> +/// closures.
> +void foreachElt(NonowningIslPtr<isl_union_pw_aff> UPwAff,
> +                const std::function<void(IslPtr<isl_pw_aff>)> &F);
> +
> +/// Enumerate all polyhedra of an isl_map.
> +///
> +/// This is a wrapper for isl_map_foreach_basic_map() that allows to
> call back
> +/// C++ closures. The callback has the possibility to interrupt (break)
> the
> +/// enumeration by returning isl_stat_error. A return value of
> isl_stat_ok will
> +/// continue enumerations, if any more elements are left.
> +///
> +/// @param UMap Collection to enumerate.
> +/// @param F    The callback function, lambda or closure.
> +///
> +/// @return The isl_stat returned by the last callback invocation;
> isl_stat_ok
> +///         if the collection was empty.
> +isl_stat
> +foreachEltWithBreak(NonowningIslPtr<isl_map> Map,
> +                    const std::function<isl_stat(IslPtr<isl_basic_map>)>
> &F);
> +
> +/// Enumerate all isl_maps of an isl_union_map.
> +///
> +/// This is a wrapper for isl_union_map_foreach_map() that allows to
> call back
> +/// C++ closures. In contrast to the variant without "_with_break", the
> callback
> +/// has the possibility to interrupt (break) the enumeration by
> returning
> +/// isl_stat_error. A return value of isl_stat_ok will continue
> enumerations, if
> +/// any more elements are left.
> +///
> +/// @param UMap Collection to enumerate.
> +/// @param F    The callback function, lambda or closure.
> +///
> +/// @return The isl_stat returned by the last callback invocation;
> isl_stat_ok
> +///         if the collection was initially empty.
> +isl_stat
> +foreachEltWithBreak(NonowningIslPtr<isl_union_map> UMap,
> +                    const std::function<isl_stat(IslPtr<isl_map> Map)>
> &F);
> +
> +/// Enumerate all pieces of an isl_pw_aff.
> +///
> +/// This is a wrapper around isl_pw_aff_foreach_piece() that allows to
> call back
> +/// C++11 closures. The callback has the possibility to interrupt
> (break) the
> +/// enumeration by returning isl_stat_error. A return value of
> isl_stat_ok will
> +/// continue enumerations, if any more elements are left.
> +///
> +/// @param UMap Collection to enumerate.
> +/// @param F    The callback function, lambda or closure.
> +///
> +/// @return The isl_stat returned by the last callback invocation;
> isl_stat_ok
> +///         if the collection was initially empty.
> +isl_stat foreachPieceWithBreak(
> +    NonowningIslPtr<isl_pw_aff> PwAff,
> +    const std::function<isl_stat(IslPtr<isl_set>, IslPtr<isl_aff>)> &F);
> +
>  } // end namespace polly
>  
>  #endif
> 
> Modified: polly/trunk/lib/CMakeLists.txt
> URL:
> http://llvm.org/viewvc/llvm-project/polly/trunk/lib/CMakeLists.txt?rev=280948&r1=280947&r2=280948&view=diff
> ==============================================================================
> --- polly/trunk/lib/CMakeLists.txt (original)
> +++ polly/trunk/lib/CMakeLists.txt Thu Sep  8 10:02:36 2016
> @@ -55,6 +55,8 @@ add_polly_library(Polly
>    Transform/CodePreparation.cpp
>    Transform/DeadCodeElimination.cpp
>    Transform/ScheduleOptimizer.cpp
> +  Transform/FlattenSchedule.cpp
> +  Transform/FlattenAlgo.cpp
>    ${POLLY_HEADER_FILES}
>    )
>  
> 
> Modified: polly/trunk/lib/Support/GICHelper.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/polly/trunk/lib/Support/GICHelper.cpp?rev=280948&r1=280947&r2=280948&view=diff
> ==============================================================================
> --- polly/trunk/lib/Support/GICHelper.cpp (original)
> +++ polly/trunk/lib/Support/GICHelper.cpp Thu Sep  8 10:02:36 2016
> @@ -198,3 +198,71 @@ std::string polly::getIslCompatibleName(
>    ValStr.erase(0, 1);
>    return getIslCompatibleName(Prefix, ValStr, Suffix);
>  }
> +
> +void polly::foreachElt(NonowningIslPtr<isl_union_map> UMap,
> +                       const std::function<void(IslPtr<isl_map> Map)>
> &F) {
> +  isl_union_map_foreach_map(
> +      UMap.keep(),
> +      [](__isl_take isl_map *Map, void *User) -> isl_stat {
> +        auto &F =
> +            *static_cast<const std::function<void(IslPtr<isl_map>)>
> *>(User);
> +        F(give(Map));
> +        return isl_stat_ok;
> +      },
> +      const_cast<void *>(static_cast<const void *>(&F)));
> +}
> +
> +void polly::foreachElt(NonowningIslPtr<isl_union_pw_aff> UPwAff,
> +                       const std::function<void(IslPtr<isl_pw_aff>)> &F)
> {
> +  isl_union_pw_aff_foreach_pw_aff(
> +      UPwAff.keep(),
> +      [](__isl_take isl_pw_aff *PwAff, void *User) -> isl_stat {
> +        auto &F =
> +            *static_cast<const std::function<void(IslPtr<isl_pw_aff>)>
> *>(User);
> +        F(give(PwAff));
> +        return isl_stat_ok;
> +      },
> +      const_cast<void *>(static_cast<const void *>(&F)));
> +}
> +
> +isl_stat polly::foreachEltWithBreak(
> +    NonowningIslPtr<isl_map> Map,
> +    const std::function<isl_stat(IslPtr<isl_basic_map>)> &F) {
> +  return isl_map_foreach_basic_map(
> +      Map.keep(),
> +      [](__isl_take isl_basic_map *BMap, void *User) -> isl_stat {
> +        auto &F = *static_cast<
> +            const std::function<isl_stat(IslPtr<isl_basic_map>)>
> *>(User);
> +        return F(give(BMap));
> +      },
> +      const_cast<void *>(static_cast<const void *>(&F)));
> +}
> +
> +isl_stat polly::foreachEltWithBreak(
> +    NonowningIslPtr<isl_union_map> UMap,
> +    const std::function<isl_stat(IslPtr<isl_map> Map)> &F) {
> +  return isl_union_map_foreach_map(
> +      UMap.keep(),
> +      [](__isl_take isl_map *Map, void *User) -> isl_stat {
> +        auto &F =
> +            *static_cast<const std::function<isl_stat(IslPtr<isl_map>
> Map)> *>(
> +                User);
> +        return F(give(Map));
> +      },
> +      const_cast<void *>(static_cast<const void *>(&F)));
> +}
> +
> +isl_stat polly::foreachPieceWithBreak(
> +    NonowningIslPtr<isl_pw_aff> PwAff,
> +    const std::function<isl_stat(IslPtr<isl_set>, IslPtr<isl_aff>)> &F)
> {
> +  return isl_pw_aff_foreach_piece(
> +      PwAff.keep(),
> +      [](__isl_take isl_set *Domain, __isl_take isl_aff *Aff,
> +         void *User) -> isl_stat {
> +        auto &F = *static_cast<
> +            const std::function<isl_stat(IslPtr<isl_set>,
> IslPtr<isl_aff>)> *>(
> +            User);
> +        return F(give(Domain), give(Aff));
> +      },
> +      const_cast<void *>(static_cast<const void *>(&F)));
> +}
> 
> Modified: polly/trunk/lib/Support/RegisterPasses.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/polly/trunk/lib/Support/RegisterPasses.cpp?rev=280948&r1=280947&r2=280948&view=diff
> ==============================================================================
> --- polly/trunk/lib/Support/RegisterPasses.cpp (original)
> +++ polly/trunk/lib/Support/RegisterPasses.cpp Thu Sep  8 10:02:36 2016
> @@ -24,6 +24,7 @@
>  #include "polly/CodeGen/CodeGeneration.h"
>  #include "polly/CodeGen/CodegenCleanup.h"
>  #include "polly/DependenceInfo.h"
> +#include "polly/FlattenSchedule.h"
>  #include "polly/LinkAllPasses.h"
>  #include "polly/Options.h"
>  #include "polly/PolyhedralInfo.h"
> @@ -180,6 +181,7 @@ void initializePollyPasses(PassRegistry
>    initializeScopInfoRegionPassPass(Registry);
>    initializeScopInfoWrapperPassPass(Registry);
>    initializeCodegenCleanupPass(Registry);
> +  initializeFlattenSchedulePass(Registry);
>  }
>  
>  /// Register Polly passes such that they form a polyhedral optimizer.
> 
> Added: polly/trunk/lib/Transform/FlattenAlgo.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/polly/trunk/lib/Transform/FlattenAlgo.cpp?rev=280948&view=auto
> ==============================================================================
> --- polly/trunk/lib/Transform/FlattenAlgo.cpp (added)
> +++ polly/trunk/lib/Transform/FlattenAlgo.cpp Thu Sep  8 10:02:36 2016
> @@ -0,0 +1,417 @@
> +//===------ FlattenAlgo.cpp ------------------------------------*- C++
> -*-===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// Main algorithm of the FlattenSchedulePass. This is a separate file to
> avoid
> +// the unittest for this requiring linking against LLVM.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#include "polly/FlattenAlgo.h"
> +#include "llvm/Support/Debug.h"
> +#define DEBUG_TYPE "polly-flatten-algo"
> +
> +using namespace polly;
> +using namespace llvm;
> +
> +namespace {
> +
> +/// Whether a dimension of a set is bounded (lower and upper) by a
> constant,
> +/// i.e. there are two constants Min and Max, such that every value x of
> the
> +/// chosen dimensions is Min <= x <= Max.
> +bool isDimBoundedByConstant(IslPtr<isl_set> Set, unsigned dim) {
> +  auto ParamDims = isl_set_dim(Set.keep(), isl_dim_param);
> +  Set = give(isl_set_project_out(Set.take(), isl_dim_param, 0,
> ParamDims));
> +  Set = give(isl_set_project_out(Set.take(), isl_dim_set, 0, dim));
> +  auto SetDims = isl_set_dim(Set.keep(), isl_dim_set);
> +  Set = give(isl_set_project_out(Set.take(), isl_dim_set, 1, SetDims -
> 1));
> +  return isl_set_is_bounded(Set.keep());
> +}
> +
> +/// Whether a dimension of a set is (lower and upper) bounded by a
> constant or
> +/// parameters, i.e. there are two expressions Min_p and Max_p of the
> parameters
> +/// p, such that every value x of the chosen dimensions is
> +/// Min_p <= x <= Max_p.
> +bool isDimBoundedByParameter(IslPtr<isl_set> Set, unsigned dim) {
> +  Set = give(isl_set_project_out(Set.take(), isl_dim_set, 0, dim));
> +  auto SetDims = isl_set_dim(Set.keep(), isl_dim_set);
> +  Set = give(isl_set_project_out(Set.take(), isl_dim_set, 1, SetDims -
> 1));
> +  return isl_set_is_bounded(Set.keep());
> +}
> +
> +/// Whether BMap's first out-dimension is not a constant.
> +bool isVariableDim(NonowningIslPtr<isl_basic_map> BMap) {
> +  auto FixedVal =
> +      give(isl_basic_map_plain_get_val_if_fixed(BMap.keep(),
> isl_dim_out, 0));
> +  return !FixedVal || isl_val_is_nan(FixedVal.keep());
> +}
> +
> +/// Whether Map's first out dimension is no constant nor piecewise
> constant.
> +bool isVariableDim(NonowningIslPtr<isl_map> Map) {
> +  return foreachEltWithBreak(Map, [](IslPtr<isl_basic_map> BMap) ->
> isl_stat {
> +    if (isVariableDim(BMap))
> +      return isl_stat_error;
> +    return isl_stat_ok;
> +  });
> +}
> +
> +/// Whether UMap's first out dimension is no (piecewise) constant.
> +bool isVariableDim(NonowningIslPtr<isl_union_map> UMap) {
> +  return foreachEltWithBreak(UMap, [](IslPtr<isl_map> Map) -> isl_stat {
> +    if (isVariableDim(Map))
> +      return isl_stat_error;
> +    return isl_stat_ok;
> +  });
> +}
> +
> +/// If @p PwAff maps to a constant, return said constant. If @p Max/@p
> Min, it
> +/// can also be a piecewise constant and it would return the
> minimum/maximum
> +/// value. Otherwise, return NaN.
> +IslPtr<isl_val> getConstant(IslPtr<isl_pw_aff> PwAff, bool Max, bool
> Min) {
> +  assert(!Max || !Min);
> +  IslPtr<isl_val> Result;
> +  foreachPieceWithBreak(
> +      PwAff, [=, &Result](IslPtr<isl_set> Set, IslPtr<isl_aff> Aff) {
> +        if (Result && isl_val_is_nan(Result.keep()))
> +          return isl_stat_ok;
> +
> +        // TODO: If Min/Max, we can also determine a minimum/maximum
> value if
> +        // Set is constant-bounded.
> +        if (!isl_aff_is_cst(Aff.keep())) {
> +          Result = give(isl_val_nan(Aff.getCtx()));
> +          return isl_stat_error;
> +        }
> +
> +        auto ThisVal = give(isl_aff_get_constant_val(Aff.keep()));
> +        if (!Result) {
> +          Result = ThisVal;
> +          return isl_stat_ok;
> +        }
> +
> +        if (isl_val_eq(Result.keep(), ThisVal.keep()))
> +          return isl_stat_ok;
> +
> +        if (Max && isl_val_gt(ThisVal.keep(), Result.keep())) {
> +          Result = ThisVal;
> +          return isl_stat_ok;
> +        }
> +
> +        if (Min && isl_val_lt(ThisVal.keep(), Result.keep())) {
> +          Result = ThisVal;
> +          return isl_stat_ok;
> +        }
> +
> +        // Not compatible
> +        Result = give(isl_val_nan(Aff.getCtx()));
> +        return isl_stat_error;
> +      });
> +  return Result;
> +}
> +
> +/// Compute @p UPwAff - @p Val.
> +IslPtr<isl_union_pw_aff> subtract(IslPtr<isl_union_pw_aff> UPwAff,
> +                                  IslPtr<isl_val> Val) {
> +  if (isl_val_is_zero(Val.keep()))
> +    return UPwAff;
> +
> +  auto Result =
> +     
> give(isl_union_pw_aff_empty(isl_union_pw_aff_get_space(UPwAff.keep())));
> +  foreachElt(UPwAff, [=, &Result](IslPtr<isl_pw_aff> PwAff) {
> +    auto ValAff = give(isl_pw_aff_val_on_domain(
> +       
> isl_set_universe(isl_space_domain(isl_pw_aff_get_space(PwAff.keep()))),
> +        Val.copy()));
> +    auto Subtracted = give(isl_pw_aff_sub(PwAff.copy(), ValAff.take()));
> +    Result = give(isl_union_pw_aff_union_add(
> +        Result.take(),
> isl_union_pw_aff_from_pw_aff(Subtracted.take())));
> +  });
> +  return Result;
> +}
> +
> +/// Compute @UPwAff * @p Val.
> +IslPtr<isl_union_pw_aff> multiply(IslPtr<isl_union_pw_aff> UPwAff,
> +                                  IslPtr<isl_val> Val) {
> +  if (isl_val_is_one(Val.keep()))
> +    return UPwAff;
> +
> +  auto Result =
> +     
> give(isl_union_pw_aff_empty(isl_union_pw_aff_get_space(UPwAff.keep())));
> +  foreachElt(UPwAff, [=, &Result](IslPtr<isl_pw_aff> PwAff) {
> +    auto ValAff = give(isl_pw_aff_val_on_domain(
> +       
> isl_set_universe(isl_space_domain(isl_pw_aff_get_space(PwAff.keep()))),
> +        Val.copy()));
> +    auto Multiplied = give(isl_pw_aff_mul(PwAff.copy(), ValAff.take()));
> +    Result = give(isl_union_pw_aff_union_add(
> +        Result.take(),
> isl_union_pw_aff_from_pw_aff(Multiplied.take())));
> +  });
> +  return Result;
> +}
> +
> +/// Remove @p n dimensions from @p UMap's range, starting at @p first.
> +///
> +/// It is assumed that all maps in the maps have at least the necessary
> number
> +/// of out dimensions.
> +IslPtr<isl_union_map> scheduleProjectOut(NonowningIslPtr<isl_union_map>
> UMap,
> +                                         unsigned first, unsigned n) {
> +  if (n == 0)
> +    return UMap; /* isl_map_project_out would also reset the tuple,
> which should
> +                    have no effect on schedule ranges */
> +
> +  auto Result =
> give(isl_union_map_empty(isl_union_map_get_space(UMap.keep())));
> +  foreachElt(UMap, [=, &Result](IslPtr<isl_map> Map) {
> +    auto Outprojected =
> +        give(isl_map_project_out(Map.take(), isl_dim_out, first, n));
> +    Result = give(isl_union_map_add_map(Result.take(),
> Outprojected.take()));
> +  });
> +  return Result;
> +}
> +
> +/// Return the number of dimensions in the input map's range.
> +///
> +/// Because this function takes an isl_union_map, the out dimensions
> could be
> +/// different. We return the maximum number in this case. However, a
> different
> +/// number of dimensions is not supported by the other code in this
> file.
> +size_t scheduleScatterDims(NonowningIslPtr<isl_union_map> Schedule) {
> +  unsigned Dims = 0;
> +  foreachElt(Schedule, [&Dims](IslPtr<isl_map> Map) {
> +    Dims = std::max(Dims, isl_map_dim(Map.keep(), isl_dim_out));
> +  });
> +  return Dims;
> +}
> +
> +/// Return the @p pos' range dimension, converted to an
> isl_union_pw_aff.
> +IslPtr<isl_union_pw_aff> scheduleExtractDimAff(IslPtr<isl_union_map>
> UMap,
> +                                               unsigned pos) {
> +  auto SingleUMap =
> +      give(isl_union_map_empty(isl_union_map_get_space(UMap.keep())));
> +  foreachElt(UMap, [=, &SingleUMap](IslPtr<isl_map> Map) {
> +    auto MapDims = isl_map_dim(Map.keep(), isl_dim_out);
> +    auto SingleMap = give(isl_map_project_out(Map.take(), isl_dim_out,
> 0, pos));
> +    SingleMap = give(isl_map_project_out(SingleMap.take(), isl_dim_out,
> 1,
> +                                         MapDims - pos - 1));
> +    SingleUMap =
> +        give(isl_union_map_add_map(SingleUMap.take(),
> SingleMap.take()));
> +  });
> +
> +  auto UAff =
> give(isl_union_pw_multi_aff_from_union_map(SingleUMap.take()));
> +  auto FirstMAff =
> +      give(isl_multi_union_pw_aff_from_union_pw_multi_aff(UAff.take()));
> +  return give(isl_multi_union_pw_aff_get_union_pw_aff(FirstMAff.keep(),
> 0));
> +}
> +
> +/// Flatten a sequence-like first dimension.
> +///
> +/// A sequence-like scatter dimension is constant, or at least only
> small
> +/// variation, typically the result of ordering a sequence of different
> +/// statements. An example would be:
> +///   { Stmt_A[] -> [0, X, ...]; Stmt_B[] -> [1, Y, ...] }
> +/// to schedule all instances of Stmt_A before any instance of Stmt_B.
> +///
> +/// To flatten, first begin with an offset of zero. Then determine the
> lowest
> +/// possible value of the dimension, call it "i" [In the example we
> start at 0].
> +/// Considering only schedules with that value, consider only instances
> with
> +/// that value and determine the extent of the next dimension. Let
> l_X(i) and
> +/// u_X(i) its minimum (lower bound) and maximum (upper bound) value.
> Add them
> +/// as "Offset + X - l_X(i)" to the new schedule, then add "u_X(i) -
> l_X(i) + 1"
> +/// to Offset and remove all i-instances from the old schedule. Repeat
> with the
> +/// remaining lowest value i' until there are no instances in the old
> schedule
> +/// left.
> +/// The example schedule would be transformed to:
> +///   { Stmt_X[] -> [X - l_X, ...]; Stmt_B -> [l_X - u_X + 1 + Y - l_Y,
> ...] }
> +IslPtr<isl_union_map> tryFlattenSequence(IslPtr<isl_union_map> Schedule)
> {
> +  auto IslCtx = Schedule.getCtx();
> +  auto ScatterSet =
> +     
> give(isl_set_from_union_set(isl_union_map_range(Schedule.copy())));
> +
> +  auto ParamSpace = give(isl_union_map_get_space(Schedule.keep()));
> +  auto Dims = isl_set_dim(ScatterSet.keep(), isl_dim_set);
> +  assert(Dims >= 2);
> +
> +  // Would cause an infinite loop.
> +  if (!isDimBoundedByConstant(ScatterSet, 0)) {
> +    DEBUG(dbgs() << "Abort; dimension is not of fixed size\n");
> +    return nullptr;
> +  }
> +
> +  auto AllDomains = give(isl_union_map_domain(Schedule.copy()));
> +  auto AllDomainsToNull =
> +      give(isl_union_pw_multi_aff_from_domain(AllDomains.take()));
> +
> +  auto NewSchedule = give(isl_union_map_empty(ParamSpace.copy()));
> +  auto Counter =
> give(isl_pw_aff_zero_on_domain(isl_local_space_from_space(
> +      isl_space_set_from_params(ParamSpace.copy()))));
> +
> +  while (!isl_set_is_empty(ScatterSet.keep())) {
> +    DEBUG(dbgs() << "Next counter:\n  " << Counter << "\n");
> +    DEBUG(dbgs() << "Remaining scatter set:\n  " << ScatterSet << "\n");
> +    auto ThisSet =
> +        give(isl_set_project_out(ScatterSet.copy(), isl_dim_set, 1, Dims
> - 1));
> +    auto ThisFirst = give(isl_set_lexmin(ThisSet.take()));
> +    auto ScatterFirst =
> +        give(isl_set_add_dims(ThisFirst.take(), isl_dim_set, Dims - 1));
> +
> +    auto SubSchedule = give(isl_union_map_intersect_range(
> +        Schedule.copy(), isl_union_set_from_set(ScatterFirst.copy())));
> +    SubSchedule = scheduleProjectOut(std::move(SubSchedule), 0, 1);
> +    SubSchedule = flattenSchedule(std::move(SubSchedule));
> +
> +    auto SubDims = scheduleScatterDims(SubSchedule);
> +    auto FirstSubSchedule = scheduleProjectOut(SubSchedule, 1, SubDims -
> 1);
> +    auto FirstScheduleAff = scheduleExtractDimAff(FirstSubSchedule, 0);
> +    auto RemainingSubSchedule =
> +        scheduleProjectOut(std::move(SubSchedule), 0, 1);
> +
> +    auto FirstSubScatter = give(
> +       
> isl_set_from_union_set(isl_union_map_range(FirstSubSchedule.take())));
> +    DEBUG(dbgs() << "Next step in sequence is:\n  " << FirstSubScatter
> << "\n");
> +
> +    if (!isDimBoundedByParameter(FirstSubScatter, 0)) {
> +      DEBUG(dbgs() << "Abort; sequence step is not bounded\n");
> +      return nullptr;
> +    }
> +
> +    auto FirstSubScatterMap =
> give(isl_map_from_range(FirstSubScatter.take()));
> +
> +    // isl_set_dim_max returns a strange isl_pw_aff with domain tuple_id
> of
> +    // 'none'. It doesn't match with any space including a 0-dimensional
> +    // anonymous tuple.
> +    // Interesting, one can create such a set using
> +    // isl_set_universe(ParamSpace). Bug?
> +    auto PartMin = give(isl_map_dim_min(FirstSubScatterMap.copy(), 0));
> +    auto PartMax = give(isl_map_dim_max(FirstSubScatterMap.take(), 0));
> +    auto One = give(isl_pw_aff_val_on_domain(
> +        isl_set_universe(isl_space_set_from_params(ParamSpace.copy())),
> +        isl_val_one(IslCtx)));
> +    auto PartLen = give(isl_pw_aff_add(
> +        isl_pw_aff_add(PartMax.take(), isl_pw_aff_neg(PartMin.copy())),
> +        One.take()));
> +
> +    auto AllPartMin = give(isl_union_pw_aff_pullback_union_pw_multi_aff(
> +        isl_union_pw_aff_from_pw_aff(PartMin.take()),
> AllDomainsToNull.copy()));
> +    auto FirstScheduleAffNormalized =
> +        give(isl_union_pw_aff_sub(FirstScheduleAff.take(),
> AllPartMin.take()));
> +    auto AllCounter = give(isl_union_pw_aff_pullback_union_pw_multi_aff(
> +        isl_union_pw_aff_from_pw_aff(Counter.copy()),
> AllDomainsToNull.copy()));
> +    auto FirstScheduleAffWithOffset = give(isl_union_pw_aff_add(
> +        FirstScheduleAffNormalized.take(), AllCounter.take()));
> +
> +    auto ScheduleWithOffset = give(isl_union_map_flat_range_product(
> +       
> isl_union_map_from_union_pw_aff(FirstScheduleAffWithOffset.take()),
> +        RemainingSubSchedule.take()));
> +    NewSchedule = give(
> +        isl_union_map_union(NewSchedule.take(),
> ScheduleWithOffset.take()));
> +
> +    ScatterSet = give(isl_set_subtract(ScatterSet.take(),
> ScatterFirst.take()));
> +    Counter = give(isl_pw_aff_add(Counter.take(), PartLen.take()));
> +  }
> +
> +  DEBUG(dbgs() << "Sequence-flatten result is:\n  " << NewSchedule <<
> "\n");
> +  return NewSchedule;
> +}
> +
> +/// Flatten a loop-like first dimension.
> +///
> +/// A loop-like dimension is one that depends on a variable (usually a
> loop's
> +/// induction variable). Let the input schedule look like this:
> +///   { Stmt[i] -> [i, X, ...] }
> +///
> +/// To flatten, we determine the largest extent of X which may not
> depend on the
> +/// actual value of i. Let l_X() the smallest possible value of X and
> u_X() its
> +/// largest value. Then, construct a new schedule
> +///   { Stmt[i] -> [i * (u_X() - l_X() + 1), ...] }
> +IslPtr<isl_union_map> tryFlattenLoop(IslPtr<isl_union_map> Schedule) {
> +  assert(scheduleScatterDims(Schedule) >= 2);
> +
> +  auto Remaining = scheduleProjectOut(Schedule, 0, 1);
> +  auto SubSchedule = flattenSchedule(Remaining);
> +  auto SubDims = scheduleScatterDims(SubSchedule);
> +
> +  auto SubExtent =
> +     
> give(isl_set_from_union_set(isl_union_map_range(SubSchedule.copy())));
> +  auto SubExtentDims = isl_set_dim(SubExtent.keep(), isl_dim_param);
> +  SubExtent = give(
> +      isl_set_project_out(SubExtent.take(), isl_dim_param, 0,
> SubExtentDims));
> +  SubExtent =
> +      give(isl_set_project_out(SubExtent.take(), isl_dim_set, 1, SubDims
> - 1));
> +
> +  if (!isDimBoundedByConstant(SubExtent, 0)) {
> +    DEBUG(dbgs() << "Abort; dimension not bounded by constant\n");
> +    return nullptr;
> +  }
> +
> +  auto Min = give(isl_set_dim_min(SubExtent.copy(), 0));
> +  DEBUG(dbgs() << "Min bound:\n  " << Min << "\n");
> +  auto MinVal = getConstant(Min, false, true);
> +  auto Max = give(isl_set_dim_max(SubExtent.take(), 0));
> +  DEBUG(dbgs() << "Max bound:\n  " << Max << "\n");
> +  auto MaxVal = getConstant(Max, true, false);
> +
> +  if (!MinVal || !MaxVal || isl_val_is_nan(MinVal.keep()) ||
> +      isl_val_is_nan(MaxVal.keep())) {
> +    DEBUG(dbgs() << "Abort; dimension bounds could not be
> determined\n");
> +    return nullptr;
> +  }
> +
> +  auto FirstSubScheduleAff = scheduleExtractDimAff(SubSchedule, 0);
> +  auto RemainingSubSchedule = scheduleProjectOut(std::move(SubSchedule),
> 0, 1);
> +
> +  auto LenVal =
> +      give(isl_val_add_ui(isl_val_sub(MaxVal.take(), MinVal.copy()),
> 1));
> +  auto FirstSubScheduleNormalized = subtract(FirstSubScheduleAff,
> MinVal);
> +
> +  // TODO: Normalize FirstAff to zero (convert to isl_map, determine
> minimum,
> +  // subtract it)
> +  auto FirstAff = scheduleExtractDimAff(Schedule, 0);
> +  auto Offset = multiply(FirstAff, LenVal);
> +  auto Index = give(
> +      isl_union_pw_aff_add(FirstSubScheduleNormalized.take(),
> Offset.take()));
> +  auto IndexMap = give(isl_union_map_from_union_pw_aff(Index.take()));
> +
> +  auto Result = give(isl_union_map_flat_range_product(
> +      IndexMap.take(), RemainingSubSchedule.take()));
> +  DEBUG(dbgs() << "Loop-flatten result is:\n  " << Result << "\n");
> +  return Result;
> +}
> +} // anonymous namespace
> +
> +IslPtr<isl_union_map> polly::flattenSchedule(IslPtr<isl_union_map>
> Schedule) {
> +  auto Dims = scheduleScatterDims(Schedule);
> +  DEBUG(dbgs() << "Recursive schedule to process:\n  " << Schedule <<
> "\n");
> +
> +  // Base case; no dimensions left
> +  if (Dims == 0) {
> +    // TODO: Add one dimension?
> +    return Schedule;
> +  }
> +
> +  // Base case; already one-dimensional
> +  if (Dims == 1)
> +    return Schedule;
> +
> +  // Fixed dimension; no need to preserve variabledness.
> +  if (!isVariableDim(Schedule)) {
> +    DEBUG(dbgs() << "Fixed dimension; try sequence flattening\n");
> +    auto NewScheduleSequence = tryFlattenSequence(Schedule);
> +    if (NewScheduleSequence)
> +      return NewScheduleSequence;
> +  }
> +
> +  // Constant stride
> +  DEBUG(dbgs() << "Try loop flattening\n");
> +  auto NewScheduleLoop = tryFlattenLoop(Schedule);
> +  if (NewScheduleLoop)
> +    return NewScheduleLoop;
> +
> +  // Try again without loop condition (may blow up the number of
> pieces!!)
> +  DEBUG(dbgs() << "Try sequence flattening again\n");
> +  auto NewScheduleSequence = tryFlattenSequence(Schedule);
> +  if (NewScheduleSequence)
> +    return NewScheduleSequence;
> +
> +  // Cannot flatten
> +  return Schedule;
> +}
> 
> Added: polly/trunk/lib/Transform/FlattenSchedule.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/polly/trunk/lib/Transform/FlattenSchedule.cpp?rev=280948&view=auto
> ==============================================================================
> --- polly/trunk/lib/Transform/FlattenSchedule.cpp (added)
> +++ polly/trunk/lib/Transform/FlattenSchedule.cpp Thu Sep  8 10:02:36
> 2016
> @@ -0,0 +1,108 @@
> +//===------ FlattenSchedule.cpp --------------------------------*- C++
> -*-===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// Try to reduce the number of scatter dimension. Useful to make
> isl_union_map
> +// schedules more understandable. This is only intended for debugging
> and
> +// unittests, not for production use.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#include "polly/FlattenSchedule.h"
> +#include "polly/FlattenAlgo.h"
> +#include "polly/ScopInfo.h"
> +#include "polly/ScopPass.h"
> +#define DEBUG_TYPE "polly-flatten-schedule"
> +
> +using namespace polly;
> +using namespace llvm;
> +
> +namespace {
> +
> +/// Print a schedule to @p OS.
> +///
> +/// Prints the schedule for each statements on a new line.
> +void printSchedule(raw_ostream &OS, NonowningIslPtr<isl_union_map>
> Schedule,
> +                   int indent) {
> +  foreachElt(Schedule, [&OS, indent](IslPtr<isl_map> Map) {
> +    OS.indent(indent) << Map << "\n";
> +  });
> +}
> +
> +/// Flatten the schedule stored in an polly::Scop.
> +class FlattenSchedule : public ScopPass {
> +private:
> +  FlattenSchedule(const FlattenSchedule &) = delete;
> +  const FlattenSchedule &operator=(const FlattenSchedule &) = delete;
> +
> +  std::shared_ptr<isl_ctx> IslCtx;
> +  IslPtr<isl_union_map> OldSchedule;
> +
> +public:
> +  static char ID;
> +  explicit FlattenSchedule() : ScopPass(ID) {}
> +
> +  virtual void getAnalysisUsage(AnalysisUsage &AU) const override {
> +    AU.addRequiredTransitive<ScopInfoRegionPass>();
> +    AU.setPreservesAll();
> +  }
> +
> +  virtual bool runOnScop(Scop &S) override {
> +    // Keep a reference to isl_ctx to ensure that it is not freed before
> we free
> +    // OldSchedule.
> +    IslCtx = S.getSharedIslCtx();
> +
> +    DEBUG(dbgs() << "Going to flatten old schedule:\n");
> +    OldSchedule = give(S.getSchedule());
> +    DEBUG(printSchedule(dbgs(), OldSchedule, 2));
> +
> +    auto Domains = give(S.getDomains());
> +    auto RestrictedOldSchedule = give(
> +        isl_union_map_intersect_domain(OldSchedule.copy(),
> Domains.copy()));
> +    DEBUG(dbgs() << "Old schedule with domains:\n");
> +    DEBUG(printSchedule(dbgs(), RestrictedOldSchedule, 2));
> +
> +    auto NewSchedule = flattenSchedule(RestrictedOldSchedule);
> +
> +    DEBUG(dbgs() << "Flattened new schedule:\n");
> +    DEBUG(printSchedule(dbgs(), NewSchedule, 2));
> +
> +    NewSchedule =
> +        give(isl_union_map_gist_domain(NewSchedule.take(),
> Domains.take()));
> +    DEBUG(dbgs() << "Gisted, flattened new schedule:\n");
> +    DEBUG(printSchedule(dbgs(), NewSchedule, 2));
> +
> +    S.setSchedule(NewSchedule.take());
> +    return false;
> +  }
> +
> +  virtual void printScop(raw_ostream &OS, Scop &S) const override {
> +    OS << "Schedule before flattening {\n";
> +    printSchedule(OS, OldSchedule, 4);
> +    OS << "}\n\n";
> +
> +    OS << "Schedule after flattening {\n";
> +    printSchedule(OS, give(S.getSchedule()), 4);
> +    OS << "}\n";
> +  }
> +
> +  virtual void releaseMemory() override {
> +    OldSchedule = nullptr;
> +    IslCtx.reset();
> +  }
> +};
> +
> +char FlattenSchedule::ID;
> +} // anonymous namespace
> +
> +Pass *polly::createFlattenSchedulePass() { return new FlattenSchedule();
> }
> +
> +INITIALIZE_PASS_BEGIN(FlattenSchedule, "polly-flatten-schedule",
> +                      "Polly - Flatten schedule", false, false)
> +INITIALIZE_PASS_END(FlattenSchedule, "polly-flatten-schedule",
> +                    "Polly - Flatten schedule", false, false)
> 
> Added: polly/trunk/test/FlattenSchedule/gemm.ll
> URL:
> http://llvm.org/viewvc/llvm-project/polly/trunk/test/FlattenSchedule/gemm.ll?rev=280948&view=auto
> ==============================================================================
> --- polly/trunk/test/FlattenSchedule/gemm.ll (added)
> +++ polly/trunk/test/FlattenSchedule/gemm.ll Thu Sep  8 10:02:36 2016
> @@ -0,0 +1,98 @@
> +; RUN: opt %loadPolly -polly-flatten-schedule -analyze < %s | FileCheck
> %s
> +;
> +; dgemm kernel
> +; C := alpha*A*B + beta*C
> +; C[ni][nj]
> +; A[ni][nk]
> +; B[nk][nj]
> +
> +target datalayout = "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32"
> +
> +define void @gemm(i32 %ni, i32 %nj, i32 %nk, double %alpha, double
> %beta, double* noalias nonnull %C, double* noalias nonnull %A, double*
> noalias nonnull %B) {
> +entry:
> +  br label %ni.for
> +
> +ni.for:
> +  %i = phi i32 [0, %entry], [%i.inc, %ni.inc]
> +  %i.cmp = icmp slt i32 %i, 3
> +  br i1 %i.cmp, label %nj.for, label %ni.exit
> +
> +  nj.for:
> +    %j = phi i32 [0, %ni.for], [%j.inc, %nj.inc]
> +    %j.cmp = icmp slt i32 %j, 7
> +    br i1 %j.cmp, label %nj_beta, label %nj.exit
> +
> +    nj_beta:
> +     %c_stride = mul nsw i32 %i, 3; %nj
> +     %c_idx_i = getelementptr inbounds double, double* %C, i32 %c_stride
> +     %c_idx_ij = getelementptr inbounds double, double* %c_idx_i, i32 %j
> +
> +     ; C[i][j] *= beta
> +     %c = load double, double* %c_idx_ij
> +     %c_beta = fmul double %c, %beta
> +     store double %c_beta, double* %c_idx_ij
> +
> +     br label %nk.for
> +
> +    nk.for:
> +      %k = phi i32 [0, %nj_beta], [%k.inc, %nk.inc]
> +      %k.cmp = icmp slt i32 %k, 3 ; %nk
> +      br i1 %k.cmp, label %nk_alpha, label %nk.exit
> +
> +      nk_alpha:
> +        %a_stride = mul nsw i32 %i, 3; %nk
> +        %a_idx_i = getelementptr inbounds double, double* %A, i32
> %a_stride
> +        %a_idx_ik = getelementptr inbounds double, double* %a_idx_i, i32
> %k
> +
> +        %b_stride = mul nsw i32 %k, 3; %nj
> +        %b_idx_k = getelementptr inbounds double, double* %B, i32
> %b_stride
> +        %b_idx_kj = getelementptr inbounds double, double* %b_idx_k, i32
> %j
> +
> +        ; C[i][j] += alpha * A[i][k] * B[k][j]
> +        %a = load double, double* %a_idx_ik
> +        %b = load double, double* %b_idx_kj
> +        %beta_c = load double, double* %c_idx_ij
> +
> +        %alpha_a = fmul double %a, %alpha
> +        %alpha_a_b = fmul double %alpha_a, %b
> +        %beta_c_alpha_a_b = fadd double %beta_c, %alpha_a_b
> +
> +        store double %beta_c_alpha_a_b, double* %c_idx_ij
> +
> +        br label %nk.inc
> +
> +    nk.inc:
> +      %k.inc = add nuw nsw i32 %k, 1
> +      br label %nk.for
> +
> +    nk.exit:
> +      ; store double %c, double* %c_idx_ij
> +      br label %nj.inc
> +
> +  nj.inc:
> +    %j.inc = add nuw nsw i32 %j, 1
> +    br label %nj.for
> +
> +  nj.exit:
> +    br label %ni.inc
> +
> +ni.inc:
> +  %i.inc = add nuw nsw i32 %i, 1
> +  br label %ni.for
> +
> +ni.exit:
> +  br label %return
> +
> +return:
> +  ret void
> +}
> +
> +
> +; CHECK:      Schedule before flattening {
> +; CHECK-NEXT:     { Stmt_nk_alpha[i0, i1, i2] -> [i0, i1, 1, i2] }
> +; CHECK-NEXT:     { Stmt_nj_beta[i0, i1] -> [i0, i1, 0, 0] }
> +; CHECK-NEXT: }
> +; CHECK:      Schedule after flattening {
> +; CHECK-NEXT:     { Stmt_nj_beta[i0, i1] -> [28i0 + 4i1] }
> +; CHECK-NEXT:     { Stmt_nk_alpha[i0, i1, i2] -> [1 + 28i0 + 4i1 + i2] }
> +; CHECK-NEXT: }
> 
> Modified: polly/trunk/test/update_check.py
> URL:
> http://llvm.org/viewvc/llvm-project/polly/trunk/test/update_check.py?rev=280948&r1=280947&r2=280948&view=diff
> ==============================================================================
> --- polly/trunk/test/update_check.py (original)
> +++ polly/trunk/test/update_check.py Thu Sep  8 10:02:36 2016
> @@ -170,6 +170,18 @@ def classyfier1(lines):
>              continue
>          elif line.startswith("New access function '"):
>              yield {'NewAccessFunction'}
> +        elif line == 'Schedule before flattening {':
> +            while True:
> +                yield  {'ScheduleBeforeFlattening'}
> +                if line == '}':
> +                    break
> +                line = i.__next__()
> +        elif line == 'Schedule after flattening {':
> +            while True:
> +                yield  {'ScheduleAfterFlattening'}
> +                if line == '}':
> +                    break
> +                line = i.__next__()
>          else:
>              yield set()
>          line = i.__next__()
> 
> Modified: polly/trunk/unittests/CMakeLists.txt
> URL:
> http://llvm.org/viewvc/llvm-project/polly/trunk/unittests/CMakeLists.txt?rev=280948&r1=280947&r2=280948&view=diff
> ==============================================================================
> --- polly/trunk/unittests/CMakeLists.txt (original)
> +++ polly/trunk/unittests/CMakeLists.txt Thu Sep  8 10:02:36 2016
> @@ -20,3 +20,4 @@ function(add_polly_unittest test_name)
>  endfunction()
>  
>  add_subdirectory(Isl)
> +add_subdirectory(Flatten)
> 
> Added: polly/trunk/unittests/Flatten/CMakeLists.txt
> URL:
> http://llvm.org/viewvc/llvm-project/polly/trunk/unittests/Flatten/CMakeLists.txt?rev=280948&view=auto
> ==============================================================================
> --- polly/trunk/unittests/Flatten/CMakeLists.txt (added)
> +++ polly/trunk/unittests/Flatten/CMakeLists.txt Thu Sep  8 10:02:36 2016
> @@ -0,0 +1,3 @@
> +add_polly_unittest(FlattenTests
> +  FlattenTest.cpp
> +  )
> 
> Added: polly/trunk/unittests/Flatten/FlattenTest.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/polly/trunk/unittests/Flatten/FlattenTest.cpp?rev=280948&view=auto
> ==============================================================================
> --- polly/trunk/unittests/Flatten/FlattenTest.cpp (added)
> +++ polly/trunk/unittests/Flatten/FlattenTest.cpp Thu Sep  8 10:02:36
> 2016
> @@ -0,0 +1,70 @@
> +//===- FlattenTest.cpp
> ----------------------------------------------------===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#include "polly/FlattenAlgo.h"
> +#include "polly/Support/GICHelper.h"
> +#include "gtest/gtest.h"
> +#include "isl/union_map.h"
> +
> +using namespace llvm;
> +using namespace polly;
> +
> +namespace {
> +
> +/// Flatten a schedule and compare to the expected result.
> +///
> +/// @param ScheduleStr The schedule to flatten as string.
> +/// @param ExpectedStr The expected result as string.
> +///
> +/// @result Whether the flattened schedule is the same as the expected
> schedule.
> +bool checkFlatten(const char *ScheduleStr, const char *ExpectedStr) {
> +  auto *Ctx = isl_ctx_alloc();
> +  isl_bool Success;
> +
> +  {
> +    auto Schedule = give(isl_union_map_read_from_str(Ctx, ScheduleStr));
> +    auto Expected = give(isl_union_map_read_from_str(Ctx, ExpectedStr));
> +
> +    auto Result = flattenSchedule(std::move(Schedule));
> +    Success = isl_union_map_is_equal(Result.keep(), Expected.keep());
> +  }
> +
> +  isl_ctx_free(Ctx);
> +  return Success == isl_bool_true;
> +}
> +
> +TEST(Flatten, FlattenTrivial) {
> +  EXPECT_TRUE(checkFlatten("{ A[] -> [0] }", "{ A[] -> [0] }"));
> +  EXPECT_TRUE(checkFlatten("{ A[i] -> [i, 0] : 0 <= i < 10 }",
> +                           "{ A[i] -> [i] : 0 <= i < 10 }"));
> +  EXPECT_TRUE(checkFlatten("{ A[i] -> [0, i] : 0 <= i < 10 }",
> +                           "{ A[i] -> [i] : 0 <= i < 10 }"));
> +}
> +
> +TEST(Flatten, FlattenSequence) {
> +  EXPECT_TRUE(checkFlatten(
> +      "[n] -> { A[i] -> [0, i] : 0 <= i < n; B[i] -> [1, i] : 0 <= i < n
> }",
> +      "[n] -> { A[i] -> [i] : 0 <= i < n; B[i] -> [n + i] : 0 <= i < n
> }"));
> +
> +  EXPECT_TRUE(checkFlatten(
> +      "{ A[i] -> [0, i] : 0 <= i < 10; B[i] -> [1, i] : 0 <= i < 10 }",
> +      "{ A[i] -> [i] : 0 <= i < 10; B[i] -> [10 + i] : 0 <= i < 10 }"));
> +}
> +
> +TEST(Flatten, FlattenLoop) {
> +  EXPECT_TRUE(checkFlatten(
> +      "[n] -> { A[i] -> [i, 0] : 0 <= i < n; B[i] -> [i, 1] : 0 <= i < n
> }",
> +      "[n] -> { A[i] -> [2i] : 0 <= i < n; B[i] -> [2i + 1] : 0 <= i < n
> }"));
> +
> +  EXPECT_TRUE(checkFlatten(
> +      "{ A[i] -> [i, 0] : 0 <= i < 10; B[i] -> [i, 1] : 0 <= i < 10 }",
> +      "{ A[i] -> [2i] : 0 <= i < 10; B[i] -> [2i + 1] : 0 <= i < 10
> }"));
> +}
> +
> +} // anonymous namespace
> 
> 
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits


More information about the llvm-commits mailing list