Files
gps/tools/local/circt/include/mlir/ExecutionEngine/SparseTensor/ArithmeticUtils.h
T
2026-07-12 11:01:51 +03:00

134 lines
4.8 KiB
C++

//===- ArithmeticUtils.h - Arithmetic helper functions ----------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// A collection of "safe" arithmetic helper methods.
//
//===----------------------------------------------------------------------===//
#ifndef MLIR_EXECUTIONENGINE_SPARSETENSOR_ARITHMETICUTILS_H
#define MLIR_EXECUTIONENGINE_SPARSETENSOR_ARITHMETICUTILS_H
#include <cassert>
#include <cinttypes>
#include <limits>
#include <type_traits>
namespace mlir {
namespace sparse_tensor {
namespace detail {
//===----------------------------------------------------------------------===//
//
// Safe comparison functions.
//
// Variants of the `==`, `!=`, `<`, `<=`, `>`, and `>=` operators which
// are careful to ensure that negatives are always considered strictly
// less than non-negatives regardless of the signedness of the types of
// the two arguments. They are "safe" in that they guarantee to *always*
// give an output and that that output is correct; in particular this means
// they never use assertions or other mechanisms for "returning an error".
//
// These functions are C++17-compatible backports of the safe comparison
// functions added in C++20, and the implementations are based on the
// sample implementations provided by the standard:
// <https://en.cppreference.com/w/cpp/utility/intcmp>.
//
//===----------------------------------------------------------------------===//
template <typename T, typename U>
constexpr bool safelyEQ(T t, U u) noexcept {
using UT = std::make_unsigned_t<T>;
using UU = std::make_unsigned_t<U>;
if constexpr (std::is_signed_v<T> == std::is_signed_v<U>)
return t == u;
else if constexpr (std::is_signed_v<T>)
return t < 0 ? false : static_cast<UT>(t) == u;
else
return u < 0 ? false : t == static_cast<UU>(u);
}
template <typename T, typename U>
constexpr bool safelyNE(T t, U u) noexcept {
return !safelyEQ(t, u);
}
template <typename T, typename U>
constexpr bool safelyLT(T t, U u) noexcept {
using UT = std::make_unsigned_t<T>;
using UU = std::make_unsigned_t<U>;
if constexpr (std::is_signed_v<T> == std::is_signed_v<U>)
return t < u;
else if constexpr (std::is_signed_v<T>)
return t < 0 ? true : static_cast<UT>(t) < u;
else
return u < 0 ? false : t < static_cast<UU>(u);
}
template <typename T, typename U>
constexpr bool safelyGT(T t, U u) noexcept {
return safelyLT(u, t);
}
template <typename T, typename U>
constexpr bool safelyLE(T t, U u) noexcept {
return !safelyGT(t, u);
}
template <typename T, typename U>
constexpr bool safelyGE(T t, U u) noexcept {
return !safelyLT(t, u);
}
//===----------------------------------------------------------------------===//
//
// Overflow checking functions.
//
// These functions use assertions to ensure correctness with respect to
// overflow/underflow. Unlike the "safe" functions above, these "checked"
// functions only guarantee that *if* they return an answer then that answer
// is correct. When assertions are enabled, they do their best to remain
// as fast as possible (since MLIR keeps assertions enabled by default,
// even for optimized builds). When assertions are disabled, they use the
// standard unchecked implementations.
//
//===----------------------------------------------------------------------===//
/// A version of `static_cast<To>` which checks for overflow/underflow.
/// The implementation avoids performing runtime assertions whenever
/// the types alone are sufficient to statically prove that overflow
/// cannot happen.
template <typename To, typename From>
[[nodiscard]] inline To checkOverflowCast(From x) {
// Check the lower bound. (For when casting from signed types.)
constexpr To minTo = std::numeric_limits<To>::min();
constexpr From minFrom = std::numeric_limits<From>::min();
if constexpr (!safelyGE(minFrom, minTo))
assert(safelyGE(x, minTo) && "cast would underflow");
// Check the upper bound.
constexpr To maxTo = std::numeric_limits<To>::max();
constexpr From maxFrom = std::numeric_limits<From>::max();
if constexpr (!safelyLE(maxFrom, maxTo))
assert(safelyLE(x, maxTo) && "cast would overflow");
// Now do the cast itself.
return static_cast<To>(x);
}
/// A version of `operator*` on `uint64_t` which guards against overflows
/// (when assertions are enabled).
inline uint64_t checkedMul(uint64_t lhs, uint64_t rhs) {
assert((lhs == 0 || rhs <= std::numeric_limits<uint64_t>::max() / lhs) &&
"Integer overflow");
return lhs * rhs;
}
} // namespace detail
} // namespace sparse_tensor
} // namespace mlir
#endif // MLIR_EXECUTIONENGINE_SPARSETENSOR_ARITHMETICUTILS_H