From 7d92e604963ee8ebac95967ff67c067def3d8484 Mon Sep 17 00:00:00 2001 From: Manuel Candales Date: Thu, 26 Jun 2025 11:03:33 -0700 Subject: [PATCH 1/2] [ET][Portable] Add util to check for overflow when casting scalar Differential Revision: [D77382647](https://our.internmc.facebook.com/intern/diff/D77382647/) [ghstack-poisoned] --- kernels/portable/cpu/scalar_utils.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/kernels/portable/cpu/scalar_utils.h b/kernels/portable/cpu/scalar_utils.h index 162f96ba85d..312a663c0e1 100644 --- a/kernels/portable/cpu/scalar_utils.h +++ b/kernels/portable/cpu/scalar_utils.h @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -287,6 +288,29 @@ inline int64_t scalar_to(const Scalar& s) { : s.to(); } +namespace internal { + +template +std::optional check_overflow_cast(From in) { + // Converting to bool can't overflow so we exclude this case from checking. + if (!std::is_same_v && c10::overflows(in)) { + return std::nullopt; + } + return static_cast(in); +} + +template +std::optional check_overflow_scalar_cast(const Scalar& in) { + if (in.isBoolean()) { + return check_overflow_cast(in.to()); + } else if (in.isFloatingPoint()) { + return check_overflow_cast(in.to()); + } else { + return check_overflow_cast(in.to()); + } +} + +} // namespace internal } // namespace utils } // namespace native } // namespace executor From c4cb0b497f7df58e663184639b8a75410f9a8367 Mon Sep 17 00:00:00 2001 From: Manuel Candales Date: Thu, 26 Jun 2025 12:51:38 -0700 Subject: [PATCH 2/2] Update on "[ET][Portable] Add util to check for overflow when casting scalar" Differential Revision: [D77382647](https://our.internmc.facebook.com/intern/diff/D77382647/) [ghstack-poisoned] --- .../core/portable_type/c10/c10/targets.bzl | 1 + .../portable_type/c10/c10/util/overflows.h | 100 ++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 runtime/core/portable_type/c10/c10/util/overflows.h diff --git a/runtime/core/portable_type/c10/c10/targets.bzl b/runtime/core/portable_type/c10/c10/targets.bzl index d64098a85fe..2311fe0216d 100644 --- a/runtime/core/portable_type/c10/c10/targets.bzl +++ b/runtime/core/portable_type/c10/c10/targets.bzl @@ -112,6 +112,7 @@ def define_common_targets(): "util/complex_utils.h", "util/floating_point_utils.h", "util/irange.h", + "util/overflows.h", ], exported_preprocessor_flags = [ "-DC10_USING_CUSTOM_GENERATED_MACROS", diff --git a/runtime/core/portable_type/c10/c10/util/overflows.h b/runtime/core/portable_type/c10/c10/util/overflows.h new file mode 100644 index 00000000000..183a2f62a32 --- /dev/null +++ b/runtime/core/portable_type/c10/c10/util/overflows.h @@ -0,0 +1,100 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include + +namespace c10 { +// In some versions of MSVC, there will be a compiler error when building. +// C4146: unary minus operator applied to unsigned type, result still unsigned +// C4804: unsafe use of type 'bool' in operation +// It can be addressed by disabling the following warning. +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4146) +#pragma warning(disable : 4804) +#pragma warning(disable : 4018) +#endif + +// The overflow checks may involve float to int conversion which may +// trigger precision loss warning. Re-enable the warning once the code +// is fixed. See T58053069. +C10_CLANG_DIAGNOSTIC_PUSH() +#if C10_CLANG_HAS_WARNING("-Wimplicit-float-conversion") +C10_CLANG_DIAGNOSTIC_IGNORE("-Wimplicit-float-conversion") +#endif + +// bool can be converted to any type. +// Without specializing on bool, in pytorch_linux_trusty_py2_7_9_build: +// `error: comparison of constant '255' with boolean expression is always false` +// for `f > limit::max()` below +template +std::enable_if_t, bool> overflows( + From /*f*/, + bool strict_unsigned [[maybe_unused]] = false) { + return false; +} + +// skip isnan and isinf check for integral types +template +std::enable_if_t && !std::is_same_v, bool> +overflows(From f, bool strict_unsigned = false) { + using limit = std::numeric_limits::type>; + if constexpr (!limit::is_signed && std::numeric_limits::is_signed) { + // allow for negative numbers to wrap using two's complement arithmetic. + // For example, with uint8, this allows for `a - b` to be treated as + // `a + 255 * b`. + if (!strict_unsigned) { + return greater_than_max(f) || + (c10::is_negative(f) && + -static_cast(f) > static_cast(limit::max())); + } + } + return c10::less_than_lowest(f) || greater_than_max(f); +} + +template +std::enable_if_t, bool> overflows( + From f, + bool strict_unsigned [[maybe_unused]] = false) { + using limit = std::numeric_limits::type>; + if (limit::has_infinity && std::isinf(static_cast(f))) { + return false; + } + if (!limit::has_quiet_NaN && (f != f)) { + return true; + } + return f < limit::lowest() || f > limit::max(); +} + +C10_CLANG_DIAGNOSTIC_POP() + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +template +std::enable_if_t::value, bool> overflows( + From f, + bool strict_unsigned = false) { + // casts from complex to real are considered to overflow if the + // imaginary component is non-zero + if (!is_complex::value && f.imag() != 0) { + return true; + } + // Check for overflow componentwise + // (Technically, the imag overflow check is guaranteed to be false + // when !is_complex, but any optimizer worth its salt will be + // able to figure it out.) + return overflows< + typename scalar_value_type::type, + typename From::value_type>(f.real(), strict_unsigned) || + overflows< + typename scalar_value_type::type, + typename From::value_type>(f.imag(), strict_unsigned); +} +} // namespace c10