CONST_ISPOWEROF2(_x); \
}))
+#define ADD_SAFE(ret, a, b) (!__builtin_add_overflow(a, b, ret))
+#define INC_SAFE(a, b) __INC_SAFE(UNIQ, a, b)
+#define __INC_SAFE(q, a, b) \
+ ({ \
+ const typeof(a) UNIQ_T(A, q) = (a); \
+ ADD_SAFE(UNIQ_T(A, q), *UNIQ_T(A, q), b); \
+ })
+
+#define SUB_SAFE(ret, a, b) (!__builtin_sub_overflow(a, b, ret))
+#define DEC_SAFE(a, b) __DEC_SAFE(UNIQ, a, b)
+#define __DEC_SAFE(q, a, b) \
+ ({ \
+ const typeof(a) UNIQ_T(A, q) = (a); \
+ SUB_SAFE(UNIQ_T(A, q), *UNIQ_T(A, q), b); \
+ })
+
+#define MUL_SAFE(ret, a, b) (!__builtin_mul_overflow(a, b, ret))
+#define MUL_ASSIGN_SAFE(a, b) __MUL_ASSIGN_SAFE(UNIQ, a, b)
+#define __MUL_ASSIGN_SAFE(q, a, b) \
+ ({ \
+ const typeof(a) UNIQ_T(A, q) = (a); \
+ MUL_SAFE(UNIQ_T(A, q), *UNIQ_T(A, q), b); \
+ })
+
#define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b))
#define __LESS_BY(aq, a, bq, b) \
({ \
const typeof(y) UNIQ_T(A, q) = (y); \
const typeof(x) UNIQ_T(B, q) = DIV_ROUND_UP((x), UNIQ_T(A, q)); \
typeof(x) UNIQ_T(C, q); \
- __builtin_mul_overflow(UNIQ_T(B, q), UNIQ_T(A, q), &UNIQ_T(C, q)) ? (typeof(x)) -1 : UNIQ_T(C, q); \
+ MUL_SAFE(&UNIQ_T(C, q), UNIQ_T(B, q), UNIQ_T(A, q)) ? UNIQ_T(C, q) : (typeof(x)) -1; \
})
#define ROUND_UP(x, y) __ROUND_UP(UNIQ, (x), (y))
#pragma GCC diagnostic pop
+#define TEST_OVERFLOW_MATH_BY_TYPE(type, min, max, lit) \
+ ({ \
+ type x; \
+ \
+ assert_se(ADD_SAFE(&x, lit(5), lit(10))); \
+ assert_se(x == lit(15)); \
+ if (IS_SIGNED_INTEGER_TYPE(type)) { \
+ assert_se(ADD_SAFE(&x, lit(5), lit(-10))); \
+ assert_se(x == lit(-5)); \
+ } \
+ assert_se(ADD_SAFE(&x, min, lit(0))); \
+ assert_se(x == min); \
+ assert_se(ADD_SAFE(&x, max, lit(0))); \
+ assert_se(x == max); \
+ if (IS_SIGNED_INTEGER_TYPE(type)) \
+ assert_se(!ADD_SAFE(&x, min, lit(-1))); \
+ assert_se(!ADD_SAFE(&x, max, lit(1))); \
+ \
+ x = lit(5); \
+ assert_se(INC_SAFE(&x, lit(10))); \
+ assert_se(x == lit(15)); \
+ if (IS_SIGNED_INTEGER_TYPE(type)) { \
+ assert_se(INC_SAFE(&x, lit(-20))); \
+ assert_se(x == lit(-5)); \
+ } \
+ x = min; \
+ assert_se(INC_SAFE(&x, lit(0))); \
+ assert_se(x == min); \
+ if (IS_SIGNED_INTEGER_TYPE(type)) \
+ assert_se(!INC_SAFE(&x, lit(-1))); \
+ x = max; \
+ assert_se(INC_SAFE(&x, lit(0))); \
+ assert_se(x == max); \
+ assert_se(!INC_SAFE(&x, lit(1))); \
+ \
+ assert_se(SUB_SAFE(&x, lit(10), lit(5))); \
+ assert_se(x == lit(5)); \
+ if (IS_SIGNED_INTEGER_TYPE(type)) { \
+ assert_se(SUB_SAFE(&x, lit(5), lit(10))); \
+ assert_se(x == lit(-5)); \
+ \
+ assert_se(SUB_SAFE(&x, lit(5), lit(-10))); \
+ assert_se(x == lit(15)); \
+ } else \
+ assert_se(!SUB_SAFE(&x, lit(5), lit(10))); \
+ assert_se(SUB_SAFE(&x, min, lit(0))); \
+ assert_se(x == min); \
+ assert_se(SUB_SAFE(&x, max, lit(0))); \
+ assert_se(x == max); \
+ assert_se(!SUB_SAFE(&x, min, lit(1))); \
+ if (IS_SIGNED_INTEGER_TYPE(type)) \
+ assert_se(!SUB_SAFE(&x, max, lit(-1))); \
+ \
+ x = lit(10); \
+ assert_se(DEC_SAFE(&x, lit(5))); \
+ assert_se(x == lit(5)); \
+ if (IS_SIGNED_INTEGER_TYPE(type)) { \
+ assert_se(DEC_SAFE(&x, lit(10))); \
+ assert_se(x == lit(-5)); \
+ \
+ x = lit(5); \
+ assert_se(DEC_SAFE(&x, lit(-10))); \
+ assert_se(x == lit(15)); \
+ } else \
+ assert_se(!DEC_SAFE(&x, lit(10))); \
+ x = min; \
+ assert_se(DEC_SAFE(&x, lit(0))); \
+ assert_se(x == min); \
+ assert_se(!DEC_SAFE(&x, lit(1))); \
+ x = max; \
+ assert_se(DEC_SAFE(&x, lit(0))); \
+ if (IS_SIGNED_INTEGER_TYPE(type)) \
+ assert_se(!DEC_SAFE(&x, lit(-1))); \
+ \
+ assert_se(MUL_SAFE(&x, lit(2), lit(4))); \
+ assert_se(x == lit(8)); \
+ if (IS_SIGNED_INTEGER_TYPE(type)) { \
+ assert_se(MUL_SAFE(&x, lit(2), lit(-4))); \
+ assert_se(x == lit(-8)); \
+ } \
+ assert_se(MUL_SAFE(&x, lit(5), lit(0))); \
+ assert_se(x == lit(0)); \
+ assert_se(MUL_SAFE(&x, min, lit(1))); \
+ assert_se(x == min); \
+ if (IS_SIGNED_INTEGER_TYPE(type)) \
+ assert_se(!MUL_SAFE(&x, min, lit(2))); \
+ assert_se(MUL_SAFE(&x, max, lit(1))); \
+ assert_se(x == max); \
+ assert_se(!MUL_SAFE(&x, max, lit(2))); \
+ \
+ x = lit(2); \
+ assert_se(MUL_ASSIGN_SAFE(&x, lit(4))); \
+ assert_se(x == lit(8)); \
+ if (IS_SIGNED_INTEGER_TYPE(type)) { \
+ assert_se(MUL_ASSIGN_SAFE(&x, lit(-1))); \
+ assert_se(x == lit(-8)); \
+ } \
+ assert_se(MUL_ASSIGN_SAFE(&x, lit(0))); \
+ assert_se(x == lit(0)); \
+ x = min; \
+ assert_se(MUL_ASSIGN_SAFE(&x, lit(1))); \
+ assert_se(x == min); \
+ if IS_SIGNED_INTEGER_TYPE(type) \
+ assert_se(!MUL_ASSIGN_SAFE(&x, lit(2))); \
+ x = max; \
+ assert_se(MUL_ASSIGN_SAFE(&x, lit(1))); \
+ assert_se(x == max); \
+ assert_se(!MUL_ASSIGN_SAFE(&x, lit(2))); \
+ })
+
+TEST(overflow_safe_math) {
+ int64_t i;
+ uint64_t u, *p;
+
+ /* basic tests */
+ TEST_OVERFLOW_MATH_BY_TYPE(int8_t, INT8_MIN, INT8_MAX, INT8_C);
+ TEST_OVERFLOW_MATH_BY_TYPE(int16_t, INT16_MIN, INT16_MAX, INT16_C);
+ TEST_OVERFLOW_MATH_BY_TYPE(int32_t, INT32_MIN, INT32_MAX, INT32_C);
+ TEST_OVERFLOW_MATH_BY_TYPE(int64_t, INT64_MIN, INT64_MAX, INT64_C);
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wtype-limits" /* Otherwise the compiler complains about comparisons to negative numbers always being false */
+ TEST_OVERFLOW_MATH_BY_TYPE(uint8_t, UINT8_C(0), UINT8_MAX, UINT8_C);
+ TEST_OVERFLOW_MATH_BY_TYPE(uint16_t, UINT16_C(0), UINT16_MAX, UINT16_C);
+ TEST_OVERFLOW_MATH_BY_TYPE(uint32_t, UINT32_C(0), UINT32_MAX, UINT32_C);
+ TEST_OVERFLOW_MATH_BY_TYPE(uint64_t, UINT64_C(0), UINT64_MAX, UINT64_C);
+#pragma GCC diagnostic pop
+
+ /* make sure we handle pointers correctly */
+ p = &u;
+ assert_se(ADD_SAFE(p, 35, 15) && (u == 50));
+ assert_se(SUB_SAFE(p, 35, 15) && (u == 20));
+ assert_se(MUL_SAFE(p, 5, 10) && (u == 50));
+ assert_se(INC_SAFE(p, 10) && (u == 60));
+ assert_se(DEC_SAFE(p, 10) && (u == 50));
+ assert_se(MUL_ASSIGN_SAFE(p, 3) && (u == 150));
+ assert_se(!ADD_SAFE(p, UINT64_MAX, 1));
+ assert_se(!SUB_SAFE(p, 0, 1));
+
+ /* cross-type sanity checks */
+ assert_se(ADD_SAFE(&i, INT32_MAX, 1));
+ assert_se(SUB_SAFE(&i, INT32_MIN, 1));
+ assert_se(!ADD_SAFE(&i, UINT64_MAX, 0));
+ assert_se(ADD_SAFE(&u, INT32_MAX, 1));
+ assert_se(MUL_SAFE(&u, INT32_MAX, 2));
+}
+
TEST(DIV_ROUND_UP) {
int div;