diff options
author | Nathan Bossart | 2024-08-15 20:47:31 +0000 |
---|---|---|
committer | Nathan Bossart | 2024-08-15 20:47:31 +0000 |
commit | 9e9a2b7031f64e49fcaf28f21a4e70eb1212165f (patch) | |
tree | e7abac509a29b705ad76d8d5123894fe3e1bf3ba /src/include/common | |
parent | ad89d71978429c61647ae57174a61deb192bd51c (diff) |
Remove dependence on -fwrapv semantics in a few places.
This commit attempts to update a few places, such as the money,
numeric, and timestamp types, to no longer rely on signed integer
wrapping for correctness. This is intended to move us closer
towards removing -fwrapv, which may enable some compiler
optimizations. However, there is presently no plan to actually
remove that compiler option in the near future.
Besides using some of the existing overflow-aware routines in
int.h, this commit introduces and makes use of some new ones.
Specifically, it adds functions that accept a signed integer and
return its absolute value as an unsigned integer with the same
width (e.g., pg_abs_s64()). It also adds functions that accept an
unsigned integer, store the result of negating that integer in a
signed integer with the same width, and return whether the negation
overflowed (e.g., pg_neg_u64_overflow()).
Finally, this commit adds a couple of tests for timestamps near
POSTGRES_EPOCH_JDATE.
Author: Joseph Koshakow
Reviewed-by: Tom Lane, Heikki Linnakangas, Jian He
Discussion: https://postgr.es/m/CAAvxfHdBPOyEGS7s%2Bxf4iaW0-cgiq25jpYdWBqQqvLtLe_t6tw%40mail.gmail.com
Diffstat (limited to 'src/include/common')
-rw-r--r-- | src/include/common/int.h | 131 |
1 files changed, 123 insertions, 8 deletions
diff --git a/src/include/common/int.h b/src/include/common/int.h index 7fc046e78af..3b1590d676f 100644 --- a/src/include/common/int.h +++ b/src/include/common/int.h @@ -23,15 +23,35 @@ /*--------- * The following guidelines apply to all the overflow routines: - * - If a + b overflows, return true, otherwise store the result of a + b - * into *result. The content of *result is implementation defined in case of - * overflow. - * - If a - b overflows, return true, otherwise store the result of a - b - * into *result. The content of *result is implementation defined in case of - * overflow. - * - If a * b overflows, return true, otherwise store the result of a * b - * into *result. The content of *result is implementation defined in case of + * + * If the result overflows, return true, otherwise store the result into + * *result. The content of *result is implementation defined in case of * overflow. + * + * bool pg_add_*_overflow(a, b, *result) + * + * Calculate a + b + * + * bool pg_sub_*_overflow(a, b, *result) + * + * Calculate a - b + * + * bool pg_mul_*_overflow(a, b, *result) + * + * Calculate a * b + * + * bool pg_neg_*_overflow(a, *result) + * + * Calculate -a + * + * + * In addition, this file contains: + * + * <unsigned int type> pg_abs_*(<signed int type> a) + * + * Calculate absolute value of a. Unlike the standard library abs() + * and labs() functions, the return type is unsigned, so the operation + * cannot overflow. *--------- */ @@ -97,6 +117,17 @@ pg_mul_s16_overflow(int16 a, int16 b, int16 *result) #endif } +static inline uint16 +pg_abs_s16(int16 a) +{ + /* + * This first widens the argument from int16 to int32 for use with abs(). + * The result is then narrowed from int32 to uint16. This prevents any + * possibility of overflow. + */ + return (uint16) abs((int32) a); +} + /* * INT32 */ @@ -154,6 +185,17 @@ pg_mul_s32_overflow(int32 a, int32 b, int32 *result) #endif } +static inline uint32 +pg_abs_s32(int32 a) +{ + /* + * This first widens the argument from int32 to int64 for use with + * i64abs(). The result is then narrowed from int64 to uint32. This + * prevents any possibility of overflow. + */ + return (uint32) i64abs((int64) a); +} + /* * INT64 */ @@ -258,6 +300,14 @@ pg_mul_s64_overflow(int64 a, int64 b, int64 *result) #endif } +static inline uint64 +pg_abs_s64(int64 a) +{ + if (unlikely(a == PG_INT64_MIN)) + return (uint64) PG_INT64_MAX + 1; + return (uint64) i64abs(a); +} + /*------------------------------------------------------------------------ * Overflow routines for unsigned integers *------------------------------------------------------------------------ @@ -318,6 +368,24 @@ pg_mul_u16_overflow(uint16 a, uint16 b, uint16 *result) #endif } +static inline bool +pg_neg_u16_overflow(uint16 a, int16 *result) +{ +#if defined(HAVE__BUILTIN_OP_OVERFLOW) + return __builtin_sub_overflow(0, a, result); +#else + int32 res = -((int32) a); + + if (unlikely(res < PG_INT16_MIN)) + { + *result = 0x5EED; /* to avoid spurious warnings */ + return true; + } + *result = res; + return false; +#endif +} + /* * INT32 */ @@ -373,6 +441,24 @@ pg_mul_u32_overflow(uint32 a, uint32 b, uint32 *result) #endif } +static inline bool +pg_neg_u32_overflow(uint32 a, int32 *result) +{ +#if defined(HAVE__BUILTIN_OP_OVERFLOW) + return __builtin_sub_overflow(0, a, result); +#else + int64 res = -((int64) a); + + if (unlikely(res < PG_INT32_MIN)) + { + *result = 0x5EED; /* to avoid spurious warnings */ + return true; + } + *result = res; + return false; +#endif +} + /* * UINT64 */ @@ -438,6 +524,35 @@ pg_mul_u64_overflow(uint64 a, uint64 b, uint64 *result) #endif } +static inline bool +pg_neg_u64_overflow(uint64 a, int64 *result) +{ +#if defined(HAVE__BUILTIN_OP_OVERFLOW) + return __builtin_sub_overflow(0, a, result); +#elif defined(HAVE_INT128) + int128 res = -((int128) a); + + if (unlikely(res < PG_INT64_MIN)) + { + *result = 0x5EED; /* to avoid spurious warnings */ + return true; + } + *result = res; + return false; +#else + if (unlikely(a > (uint64) PG_INT64_MAX + 1)) + { + *result = 0x5EED; /* to avoid spurious warnings */ + return true; + } + if (unlikely(a == (uint64) PG_INT64_MAX + 1)) + *result = PG_INT64_MIN; + else + *result = -((int64) a); + return false; +#endif +} + /*------------------------------------------------------------------------ * * Comparison routines for integer types. |