@@ -40,6 +40,50 @@ namespace tflite {
4040
4141constexpr int kReverseShift = -1 ;
4242
43+ // Well-defined wrapping integer arithmetic helpers.
44+ //
45+ // Several elementwise/reduction kernels intentionally allow their value
46+ // arithmetic to wrap around for narrow integer types (the result is later
47+ // clamped, or wrapping is the documented behavior). Performing that wrap
48+ // directly on signed types is Undefined Behavior in C++, which the optimizer is
49+ // free to miscompile. These helpers instead perform the arithmetic in the
50+ // corresponding unsigned type -- where wrapping is fully defined -- and convert
51+ // the result back, so there is no UB even in optimized release builds.
52+ //
53+ // For floating-point T these are plain a+b / a*b (no overflow concern); the
54+ // unsigned path is only taken for integral types.
55+ //
56+ // Note on integral promotion: for integer types narrower than int (e.g.
57+ // int16_t), the corresponding unsigned type (uint16_t) is still promoted to the
58+ // signed `int` before the arithmetic is performed. That promotion would
59+ // reintroduce signed overflow UB (e.g. 65535 * 65535 overflows int). To avoid
60+ // it we widen the operands to an unsigned type at least as wide as unsigned int
61+ // so the arithmetic itself stays unsigned, then truncate back down to the
62+ // narrow type -- both steps are well-defined.
63+ template <typename T>
64+ inline T WrappingAdd (T a, T b) {
65+ if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool >) {
66+ using U = std::make_unsigned_t <T>;
67+ using P = std::common_type_t <U, unsigned int >;
68+ return static_cast <T>(static_cast <U>(static_cast <P>(static_cast <U>(a)) +
69+ static_cast <P>(static_cast <U>(b))));
70+ } else {
71+ return a + b;
72+ }
73+ }
74+
75+ template <typename T>
76+ inline T WrappingMul (T a, T b) {
77+ if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool >) {
78+ using U = std::make_unsigned_t <T>;
79+ using P = std::common_type_t <U, unsigned int >;
80+ return static_cast <T>(static_cast <U>(static_cast <P>(static_cast <U>(a)) *
81+ static_cast <P>(static_cast <U>(b))));
82+ } else {
83+ return a * b;
84+ }
85+ }
86+
4387// Reduces and compresses dimensions so that broadcast handling becomes more
4488// efficient. Returns true if the output shape is broadcastable; it doesn't
4589// contain any degenerate dimension, i.e. shape dimension = 0. False otherwise.
0 commit comments