//===- Support/GICHelper.h -- Helper functions for ISL --------------------===//
 
//
 
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 
// See https://llvm.org/LICENSE.txt for license information.
 
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
//
 
//===----------------------------------------------------------------------===//
 
//
 
// Helper functions for isl objects.
 
//
 
//===----------------------------------------------------------------------===//
 
//
 
#ifndef POLLY_SUPPORT_GIC_HELPER_H
 
#define POLLY_SUPPORT_GIC_HELPER_H
 
 
 
#include "llvm/ADT/APInt.h"
 
#include "llvm/IR/DiagnosticInfo.h"
 
#include "llvm/Support/raw_ostream.h"
 
#include "isl/ctx.h"
 
#include "isl/isl-noexceptions.h"
 
#include "isl/options.h"
 
 
 
namespace polly {
 
 
 
/// Translate an llvm::APInt to an isl_val.
 
///
 
/// Translate the bitsequence without sign information as provided by APInt into
 
/// a signed isl_val type. Depending on the value of @p IsSigned @p Int is
 
/// interpreted as unsigned value or as signed value in two's complement
 
/// representation.
 
///
 
/// Input IsSigned                 Output
 
///
 
///     0        0           ->    0
 
///     1        0           ->    1
 
///    00        0           ->    0
 
///    01        0           ->    1
 
///    10        0           ->    2
 
///    11        0           ->    3
 
///
 
///     0        1           ->    0
 
///     1        1           ->   -1
 
///    00        1           ->    0
 
///    01        1           ->    1
 
///    10        1           ->   -2
 
///    11        1           ->   -1
 
///
 
/// @param Ctx      The isl_ctx to create the isl_val in.
 
/// @param Int      The integer value to translate.
 
/// @param IsSigned If the APInt should be interpreted as signed or unsigned
 
///                 value.
 
///
 
/// @return The isl_val corresponding to @p Int.
 
__isl_give isl_val *isl_valFromAPInt(isl_ctx *Ctx, const llvm::APInt Int,
 
                                     bool IsSigned);
 
 
 
/// Translate an llvm::APInt to an isl::val.
 
///
 
/// Translate the bitsequence without sign information as provided by APInt into
 
/// a signed isl::val type. Depending on the value of @p IsSigned @p Int is
 
/// interpreted as unsigned value or as signed value in two's complement
 
/// representation.
 
///
 
/// Input IsSigned                 Output
 
///
 
///     0        0           ->    0
 
///     1        0           ->    1
 
///    00        0           ->    0
 
///    01        0           ->    1
 
///    10        0           ->    2
 
///    11        0           ->    3
 
///
 
///     0        1           ->    0
 
///     1        1           ->   -1
 
///    00        1           ->    0
 
///    01        1           ->    1
 
///    10        1           ->   -2
 
///    11        1           ->   -1
 
///
 
/// @param Ctx      The isl_ctx to create the isl::val in.
 
/// @param Int      The integer value to translate.
 
/// @param IsSigned If the APInt should be interpreted as signed or unsigned
 
///                 value.
 
///
 
/// @return The isl::val corresponding to @p Int.
 
inline isl::val valFromAPInt(isl_ctx *Ctx, const llvm::APInt Int,
 
                             bool IsSigned) {
 
  return isl::manage(isl_valFromAPInt(Ctx, Int, IsSigned));
 
}
 
 
 
/// Translate isl_val to llvm::APInt.
 
///
 
/// This function can only be called on isl_val values which are integers.
 
/// Calling this function with a non-integral rational, NaN or infinity value
 
/// is not allowed.
 
///
 
/// As the input isl_val may be negative, the APInt that this function returns
 
/// must always be interpreted as signed two's complement value. The bitwidth of
 
/// the generated APInt is always the minimal bitwidth necessary to model the
 
/// provided integer when interpreting the bit pattern as signed value.
 
///
 
/// Some example conversions are:
 
///
 
///   Input      Bits    Signed  Bitwidth
 
///       0 ->      0         0         1
 
///      -1 ->      1        -1         1
 
///       1 ->     01         1         2
 
///      -2 ->     10        -2         2
 
///       2 ->    010         2         3
 
///      -3 ->    101        -3         3
 
///       3 ->    011         3         3
 
///      -4 ->    100        -4         3
 
///       4 ->   0100         4         4
 
///
 
/// @param Val The isl val to translate.
 
///
 
/// @return The APInt value corresponding to @p Val.
 
llvm::APInt APIntFromVal(__isl_take isl_val *Val);
 
 
 
/// Translate isl::val to llvm::APInt.
 
///
 
/// This function can only be called on isl::val values which are integers.
 
/// Calling this function with a non-integral rational, NaN or infinity value
 
/// is not allowed.
 
///
 
/// As the input isl::val may be negative, the APInt that this function returns
 
/// must always be interpreted as signed two's complement value. The bitwidth of
 
/// the generated APInt is always the minimal bitwidth necessary to model the
 
/// provided integer when interpreting the bit pattern as signed value.
 
///
 
/// Some example conversions are:
 
///
 
///   Input      Bits    Signed  Bitwidth
 
///       0 ->      0         0         1
 
///      -1 ->      1        -1         1
 
///       1 ->     01         1         2
 
///      -2 ->     10        -2         2
 
///       2 ->    010         2         3
 
///      -3 ->    101        -3         3
 
///       3 ->    011         3         3
 
///      -4 ->    100        -4         3
 
///       4 ->   0100         4         4
 
///
 
/// @param Val The isl val to translate.
 
///
 
/// @return The APInt value corresponding to @p Val.
 
inline llvm::APInt APIntFromVal(isl::val V) {
 
  return APIntFromVal(V.release());
 
}
 
 
 
/// Get c++ string from Isl objects.
 
//@{
 
#define ISL_CPP_OBJECT_TO_STRING(name)                                         \
 
  inline std::string stringFromIslObj(const name &Obj,                         \
 
                                      std::string DefaultValue = "") {         \
 
    return stringFromIslObj(Obj.get(), DefaultValue);                          \
 
  }
 
 
 
#define ISL_OBJECT_TO_STRING(name)                                             \
 
  std::string stringFromIslObj(__isl_keep isl_##name *Obj,                     \
 
                               std::string DefaultValue = "");                 \
 
  ISL_CPP_OBJECT_TO_STRING(isl::name)
 
 
 
ISL_OBJECT_TO_STRING(aff)
 
ISL_OBJECT_TO_STRING(ast_expr)
 
ISL_OBJECT_TO_STRING(ast_node)
 
ISL_OBJECT_TO_STRING(basic_map)
 
ISL_OBJECT_TO_STRING(basic_set)
 
ISL_OBJECT_TO_STRING(map)
 
ISL_OBJECT_TO_STRING(set)
 
ISL_OBJECT_TO_STRING(id)
 
ISL_OBJECT_TO_STRING(multi_aff)
 
ISL_OBJECT_TO_STRING(multi_pw_aff)
 
ISL_OBJECT_TO_STRING(multi_union_pw_aff)
 
ISL_OBJECT_TO_STRING(point)
 
ISL_OBJECT_TO_STRING(pw_aff)
 
ISL_OBJECT_TO_STRING(pw_multi_aff)
 
ISL_OBJECT_TO_STRING(schedule)
 
ISL_OBJECT_TO_STRING(schedule_node)
 
ISL_OBJECT_TO_STRING(space)
 
ISL_OBJECT_TO_STRING(union_access_info)
 
ISL_OBJECT_TO_STRING(union_flow)
 
ISL_OBJECT_TO_STRING(union_set)
 
ISL_OBJECT_TO_STRING(union_map)
 
ISL_OBJECT_TO_STRING(union_pw_aff)
 
ISL_OBJECT_TO_STRING(union_pw_multi_aff)
 
//@}
 
 
 
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
 
/// C++ wrapper for isl_*_dump() functions.
 
//@{
 
 
 
#define ISL_DUMP_OBJECT(name)                                                  \
 
  void dumpIslObj(const isl::name &Obj);                                       \
 
  void dumpIslObj(isl_##name *Obj);
 
 
 
ISL_DUMP_OBJECT(aff)
 
ISL_DUMP_OBJECT(aff_list)
 
ISL_DUMP_OBJECT(ast_expr)
 
ISL_DUMP_OBJECT(ast_node)
 
ISL_DUMP_OBJECT(ast_node_list)
 
ISL_DUMP_OBJECT(basic_map)
 
ISL_DUMP_OBJECT(basic_map_list)
 
ISL_DUMP_OBJECT(basic_set)
 
ISL_DUMP_OBJECT(basic_set_list)
 
ISL_DUMP_OBJECT(constraint)
 
ISL_DUMP_OBJECT(id)
 
ISL_DUMP_OBJECT(id_list)
 
ISL_DUMP_OBJECT(id_to_ast_expr)
 
ISL_DUMP_OBJECT(local_space)
 
ISL_DUMP_OBJECT(map)
 
ISL_DUMP_OBJECT(map_list)
 
ISL_DUMP_OBJECT(multi_aff)
 
ISL_DUMP_OBJECT(multi_pw_aff)
 
ISL_DUMP_OBJECT(multi_union_pw_aff)
 
ISL_DUMP_OBJECT(multi_val)
 
ISL_DUMP_OBJECT(point)
 
ISL_DUMP_OBJECT(pw_aff)
 
ISL_DUMP_OBJECT(pw_aff_list)
 
ISL_DUMP_OBJECT(pw_multi_aff)
 
ISL_DUMP_OBJECT(schedule)
 
ISL_DUMP_OBJECT(schedule_constraints)
 
ISL_DUMP_OBJECT(schedule_node)
 
ISL_DUMP_OBJECT(set)
 
ISL_DUMP_OBJECT(set_list)
 
ISL_DUMP_OBJECT(space)
 
ISL_DUMP_OBJECT(union_map)
 
ISL_DUMP_OBJECT(union_pw_aff)
 
ISL_DUMP_OBJECT(union_pw_aff_list)
 
ISL_DUMP_OBJECT(union_pw_multi_aff)
 
ISL_DUMP_OBJECT(union_set)
 
ISL_DUMP_OBJECT(union_set_list)
 
ISL_DUMP_OBJECT(val)
 
ISL_DUMP_OBJECT(val_list)
 
//@}
 
 
 
/// Emit the equivaltent of the isl_*_dump output into a raw_ostream.
 
/// @{
 
void dumpIslObj(const isl::schedule_node &Node, llvm::raw_ostream &OS);
 
void dumpIslObj(__isl_keep isl_schedule_node *node, llvm::raw_ostream &OS);
 
/// @}
 
#endif
 
 
 
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
 
                                     __isl_keep isl_union_map *Map) {
 
  OS << polly::stringFromIslObj(Map, "null");
 
  return OS;
 
}
 
 
 
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
 
                                     __isl_keep isl_map *Map) {
 
  OS << polly::stringFromIslObj(Map, "null");
 
  return OS;
 
}
 
 
 
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
 
                                     __isl_keep isl_set *Set) {
 
  OS << polly::stringFromIslObj(Set, "null");
 
  return OS;
 
}
 
 
 
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
 
                                     __isl_keep isl_pw_aff *Map) {
 
  OS << polly::stringFromIslObj(Map, "null");
 
  return OS;
 
}
 
 
 
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
 
                                     __isl_keep isl_pw_multi_aff *PMA) {
 
  OS << polly::stringFromIslObj(PMA, "null");
 
  return OS;
 
}
 
 
 
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
 
                                     __isl_keep isl_multi_aff *MA) {
 
  OS << polly::stringFromIslObj(MA, "null");
 
  return OS;
 
}
 
 
 
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
 
                                     __isl_keep isl_union_pw_multi_aff *UPMA) {
 
  OS << polly::stringFromIslObj(UPMA, "null");
 
  return OS;
 
}
 
 
 
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
 
                                     __isl_keep isl_schedule *Schedule) {
 
  OS << polly::stringFromIslObj(Schedule, "null");
 
  return OS;
 
}
 
 
 
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
 
                                     __isl_keep isl_space *Space) {
 
  OS << polly::stringFromIslObj(Space, "null");
 
  return OS;
 
}
 
 
 
/// Combine Prefix, Val (or Number) and Suffix to an isl-compatible name.
 
///
 
/// In case @p UseInstructionNames is set, this function returns:
 
///
 
/// @p Prefix + "_" + @p Val->getName() + @p Suffix
 
///
 
/// otherwise
 
///
 
/// @p Prefix + to_string(Number) + @p Suffix
 
///
 
/// We ignore the value names by default, as they may change between release
 
/// and debug mode and can consequently not be used when aiming for reproducible
 
/// builds. However, for debugging named statements are often helpful, hence
 
/// we allow their optional use.
 
std::string getIslCompatibleName(const std::string &Prefix,
 
                                 const llvm::Value *Val, long Number,
 
                                 const std::string &Suffix,
 
                                 bool UseInstructionNames);
 
 
 
/// Combine Prefix, Name (or Number) and Suffix to an isl-compatible name.
 
///
 
/// In case @p UseInstructionNames is set, this function returns:
 
///
 
/// @p Prefix + "_" + Name + @p Suffix
 
///
 
/// otherwise
 
///
 
/// @p Prefix + to_string(Number) + @p Suffix
 
///
 
/// We ignore @p Name by default, as they may change between release
 
/// and debug mode and can consequently not be used when aiming for reproducible
 
/// builds. However, for debugging named statements are often helpful, hence
 
/// we allow their optional use.
 
std::string getIslCompatibleName(const std::string &Prefix,
 
                                 const std::string &Middle, long Number,
 
                                 const std::string &Suffix,
 
                                 bool UseInstructionNames);
 
 
 
std::string getIslCompatibleName(const std::string &Prefix,
 
                                 const std::string &Middle,
 
                                 const std::string &Suffix);
 
 
 
inline llvm::DiagnosticInfoOptimizationBase &
 
operator<<(llvm::DiagnosticInfoOptimizationBase &OS,
 
           const isl::union_map &Obj) {
 
  OS << stringFromIslObj(Obj);
 
  return OS;
 
}
 
 
 
/// Scope guard for code that allows arbitrary isl function to return an error
 
/// if the max-operations quota exceeds.
 
///
 
/// This allows to opt-in code sections that have known long executions times.
 
/// code not in a hot path can continue to assume that no unexpected error
 
/// occurs.
 
///
 
/// This is typically used inside a nested IslMaxOperationsGuard scope. The
 
/// IslMaxOperationsGuard defines the number of allowed base operations for some
 
/// code, IslQuotaScope defines where it is allowed to return an error result.
 
class IslQuotaScope final {
 
  isl_ctx *IslCtx;
 
  int OldOnError;
 
 
 
public:
 
  IslQuotaScope() : IslCtx(nullptr) {}
 
  IslQuotaScope(const IslQuotaScope &) = delete;
 
  IslQuotaScope(IslQuotaScope &&Other)
 
      : IslCtx(Other.IslCtx), OldOnError(Other.OldOnError) {
 
    Other.IslCtx = nullptr;
 
  }
 
  const IslQuotaScope &operator=(IslQuotaScope &&Other) {
 
    std::swap(this->IslCtx, Other.IslCtx);
 
    std::swap(this->OldOnError, Other.OldOnError);
 
    return *this;
 
  }
 
 
 
  /// Enter a quota-aware scope.
 
  ///
 
  /// Should not be used directly. Use IslMaxOperationsGuard::enter() instead.
 
  explicit IslQuotaScope(isl_ctx *IslCtx, unsigned long LocalMaxOps)
 
      : IslCtx(IslCtx) {
 
    assert(IslCtx);
 
    assert(isl_ctx_get_max_operations(IslCtx) == 0 && "Incorrect nesting");
 
    if (LocalMaxOps == 0) {
 
      this->IslCtx = nullptr;
 
      return;
 
    }
 
 
 
    OldOnError = isl_options_get_on_error(IslCtx);
 
    isl_options_set_on_error(IslCtx, ISL_ON_ERROR_CONTINUE);
 
    isl_ctx_reset_error(IslCtx);
 
    isl_ctx_set_max_operations(IslCtx, LocalMaxOps);
 
  }
 
 
 
  ~IslQuotaScope() {
 
    if (!IslCtx)
 
      return;
 
 
 
    assert(isl_ctx_get_max_operations(IslCtx) > 0 && "Incorrect nesting");
 
    assert(isl_options_get_on_error(IslCtx) == ISL_ON_ERROR_CONTINUE &&
 
           "Incorrect nesting");
 
    isl_ctx_set_max_operations(IslCtx, 0);
 
    isl_options_set_on_error(IslCtx, OldOnError);
 
  }
 
 
 
  /// Return whether the current quota has exceeded.
 
  bool hasQuotaExceeded() const {
 
    if (!IslCtx)
 
      return false;
 
 
 
    return isl_ctx_last_error(IslCtx) == isl_error_quota;
 
  }
 
};
 
 
 
/// Scoped limit of ISL operations.
 
///
 
/// Limits the number of ISL operations during the lifetime of this object. The
 
/// idea is to use this as an RAII guard for the scope where the code is aware
 
/// that ISL can return errors even when all input is valid. After leaving the
 
/// scope, it will return to the error setting as it was before. That also means
 
/// that the error setting should not be changed while in that scope.
 
///
 
/// Such scopes are not allowed to be nested because the previous operations
 
/// counter cannot be reset to the previous state, or one that adds the
 
/// operations while being in the nested scope. Use therefore is only allowed
 
/// while currently a no operations-limit is active.
 
class IslMaxOperationsGuard final {
 
private:
 
  /// The ISL context to set the operations limit.
 
  ///
 
  /// If set to nullptr, there is no need for any action at the end of the
 
  /// scope.
 
  isl_ctx *IslCtx;
 
 
 
  /// Maximum number of operations for the scope.
 
  unsigned long LocalMaxOps;
 
 
 
  /// When AutoEnter is enabled, holds the IslQuotaScope object.
 
  IslQuotaScope TopLevelScope;
 
 
 
public:
 
  /// Enter a max operations scope.
 
  ///
 
  /// @param IslCtx      The ISL context to set the operations limit for.
 
  /// @param LocalMaxOps Maximum number of operations allowed in the
 
  ///                    scope. If set to zero, no operations limit is enforced.
 
  /// @param AutoEnter   If true, automatically enters an IslQuotaScope such
 
  ///                    that isl operations may return quota errors
 
  ///                    immediately. If false, only starts the operations
 
  ///                    counter, but isl does not return quota errors before
 
  ///                    calling enter().
 
  IslMaxOperationsGuard(isl_ctx *IslCtx, unsigned long LocalMaxOps,
 
                        bool AutoEnter = true)
 
      : IslCtx(IslCtx), LocalMaxOps(LocalMaxOps) {
 
    assert(IslCtx);
 
    assert(isl_ctx_get_max_operations(IslCtx) == 0 &&
 
           "Nested max operations not supported");
 
 
 
    // Users of this guard may check whether the last error was isl_error_quota.
 
    // Reset the last error such that a previous out-of-quota error is not
 
    // mistaken to have occurred in the in this quota, even if the max number of
 
    // operations is set to infinite (LocalMaxOps == 0).
 
    isl_ctx_reset_error(IslCtx);
 
 
 
    if (LocalMaxOps == 0) {
 
      // No limit on operations; also disable restoring on_error/max_operations.
 
      this->IslCtx = nullptr;
 
      return;
 
    }
 
 
 
    isl_ctx_reset_operations(IslCtx);
 
    TopLevelScope = enter(AutoEnter);
 
  }
 
 
 
  /// Enter a scope that can handle out-of-quota errors.
 
  ///
 
  /// @param AllowReturnNull Whether the scoped code can handle out-of-quota
 
  ///                        errors. If false, returns a dummy scope object that
 
  ///                        does nothing.
 
  IslQuotaScope enter(bool AllowReturnNull = true) {
 
    return AllowReturnNull && IslCtx ? IslQuotaScope(IslCtx, LocalMaxOps)
 
                                     : IslQuotaScope();
 
  }
 
 
 
  /// Return whether the current quota has exceeded.
 
  bool hasQuotaExceeded() const {
 
    if (!IslCtx)
 
      return false;
 
 
 
    return isl_ctx_last_error(IslCtx) == isl_error_quota;
 
  }
 
};
 
} // end namespace polly
 
 
 
#endif