clang 20.0.0 (based on r547379) from build 12806354. Bug: http://b/379133546 Test: N/A Change-Id: I2eb8938af55d809de674be63cb30cf27e801862b Upstream-Commit: ad834e67b1105d15ef907f6255d4c96e8e733f57
493 lines
17 KiB
C++
493 lines
17 KiB
C++
//===- 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
|