//===- 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 #include #include #include 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: // . // //===----------------------------------------------------------------------===// template constexpr bool safelyEQ(T t, U u) noexcept { using UT = std::make_unsigned_t; using UU = std::make_unsigned_t; if constexpr (std::is_signed_v == std::is_signed_v) return t == u; else if constexpr (std::is_signed_v) return t < 0 ? false : static_cast(t) == u; else return u < 0 ? false : t == static_cast(u); } template constexpr bool safelyNE(T t, U u) noexcept { return !safelyEQ(t, u); } template constexpr bool safelyLT(T t, U u) noexcept { using UT = std::make_unsigned_t; using UU = std::make_unsigned_t; if constexpr (std::is_signed_v == std::is_signed_v) return t < u; else if constexpr (std::is_signed_v) return t < 0 ? true : static_cast(t) < u; else return u < 0 ? false : t < static_cast(u); } template constexpr bool safelyGT(T t, U u) noexcept { return safelyLT(u, t); } template constexpr bool safelyLE(T t, U u) noexcept { return !safelyGT(t, u); } template 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` 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 [[nodiscard]] inline To checkOverflowCast(From x) { // Check the lower bound. (For when casting from signed types.) constexpr To minTo = std::numeric_limits::min(); constexpr From minFrom = std::numeric_limits::min(); if constexpr (!safelyGE(minFrom, minTo)) assert(safelyGE(x, minTo) && "cast would underflow"); // Check the upper bound. constexpr To maxTo = std::numeric_limits::max(); constexpr From maxFrom = std::numeric_limits::max(); if constexpr (!safelyLE(maxFrom, maxTo)) assert(safelyLE(x, maxTo) && "cast would overflow"); // Now do the cast itself. return static_cast(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::max() / lhs) && "Integer overflow"); return lhs * rhs; } } // namespace detail } // namespace sparse_tensor } // namespace mlir #endif // MLIR_EXECUTIONENGINE_SPARSETENSOR_ARITHMETICUTILS_H