123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 |
- /*
- * Copyright (c) 2019 Facebook.
- *
- * SPDX-License-Identifier: Apache-2.0
- */
- /**
- * @file
- * @brief Inline implementation of functions declared in math_extras.h.
- */
- #ifndef ZEPHYR_INCLUDE_SYS_MATH_EXTRAS_H_
- #error "please include <sys/math_extras.h> instead of this file"
- #endif
- #include <toolchain.h>
- /*
- * Force the use of portable C code (no builtins) by defining
- * PORTABLE_MISC_MATH_EXTRAS before including <misc/math_extras.h>.
- * This is primarily for use by tests.
- *
- * We'll #undef use_builtin again at the end of the file.
- */
- #ifdef PORTABLE_MISC_MATH_EXTRAS
- #define use_builtin(x) 0
- #else
- #define use_builtin(x) HAS_BUILTIN(x)
- #endif
- #if use_builtin(__builtin_add_overflow)
- static inline bool u16_add_overflow(uint16_t a, uint16_t b, uint16_t *result)
- {
- return __builtin_add_overflow(a, b, result);
- }
- static inline bool u32_add_overflow(uint32_t a, uint32_t b, uint32_t *result)
- {
- return __builtin_add_overflow(a, b, result);
- }
- static inline bool u64_add_overflow(uint64_t a, uint64_t b, uint64_t *result)
- {
- return __builtin_add_overflow(a, b, result);
- }
- static inline bool size_add_overflow(size_t a, size_t b, size_t *result)
- {
- return __builtin_add_overflow(a, b, result);
- }
- #else /* !use_builtin(__builtin_add_overflow) */
- static inline bool u16_add_overflow(uint16_t a, uint16_t b, uint16_t *result)
- {
- uint16_t c = a + b;
- *result = c;
- return c < a;
- }
- static inline bool u32_add_overflow(uint32_t a, uint32_t b, uint32_t *result)
- {
- uint32_t c = a + b;
- *result = c;
- return c < a;
- }
- static inline bool u64_add_overflow(uint64_t a, uint64_t b, uint64_t *result)
- {
- uint64_t c = a + b;
- *result = c;
- return c < a;
- }
- static inline bool size_add_overflow(size_t a, size_t b, size_t *result)
- {
- size_t c = a + b;
- *result = c;
- return c < a;
- }
- #endif /* use_builtin(__builtin_add_overflow) */
- #if use_builtin(__builtin_mul_overflow)
- static inline bool u16_mul_overflow(uint16_t a, uint16_t b, uint16_t *result)
- {
- return __builtin_mul_overflow(a, b, result);
- }
- static inline bool u32_mul_overflow(uint32_t a, uint32_t b, uint32_t *result)
- {
- return __builtin_mul_overflow(a, b, result);
- }
- static inline bool u64_mul_overflow(uint64_t a, uint64_t b, uint64_t *result)
- {
- return __builtin_mul_overflow(a, b, result);
- }
- static inline bool size_mul_overflow(size_t a, size_t b, size_t *result)
- {
- return __builtin_mul_overflow(a, b, result);
- }
- #else /* !use_builtin(__builtin_mul_overflow) */
- static inline bool u16_mul_overflow(uint16_t a, uint16_t b, uint16_t *result)
- {
- uint16_t c = a * b;
- *result = c;
- return a != 0 && (c / a) != b;
- }
- static inline bool u32_mul_overflow(uint32_t a, uint32_t b, uint32_t *result)
- {
- uint32_t c = a * b;
- *result = c;
- return a != 0 && (c / a) != b;
- }
- static inline bool u64_mul_overflow(uint64_t a, uint64_t b, uint64_t *result)
- {
- uint64_t c = a * b;
- *result = c;
- return a != 0 && (c / a) != b;
- }
- static inline bool size_mul_overflow(size_t a, size_t b, size_t *result)
- {
- size_t c = a * b;
- *result = c;
- return a != 0 && (c / a) != b;
- }
- #endif /* use_builtin(__builtin_mul_overflow) */
- /*
- * The GCC builtins __builtin_clz(), __builtin_ctz(), and 64-bit
- * variants are described by the GCC documentation as having undefined
- * behavior when the argument is zero. See
- * https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html.
- *
- * The undefined behavior applies to all architectures, regardless of
- * the behavior of the instruction used to implement the builtin.
- *
- * We don't want to expose users of this API to the undefined behavior,
- * so we use a conditional to explicitly provide the correct result when
- * x=0.
- *
- * Most instruction set architectures have a CLZ instruction or similar
- * that already computes the correct result for x=0. Both GCC and Clang
- * know this and simply generate a CLZ instruction, optimizing away the
- * conditional.
- *
- * For x86, and for compilers that fail to eliminate the conditional,
- * there is often another opportunity for optimization since code using
- * these functions tends to contain a zero check already. For example,
- * from kernel/sched.c:
- *
- * struct k_thread *z_priq_mq_best(struct _priq_mq *pq)
- * {
- * if (!pq->bitmask) {
- * return NULL;
- * }
- *
- * struct k_thread *thread = NULL;
- * sys_dlist_t *l =
- * &pq->queues[u32_count_trailing_zeros(pq->bitmask)];
- *
- * ...
- *
- * The compiler will often be able to eliminate the redundant x == 0
- * check after inlining the call to u32_count_trailing_zeros().
- */
- #if use_builtin(__builtin_clz)
- static inline int u32_count_leading_zeros(uint32_t x)
- {
- return x == 0 ? 32 : __builtin_clz(x);
- }
- #else /* !use_builtin(__builtin_clz) */
- static inline int u32_count_leading_zeros(uint32_t x)
- {
- int b;
- for (b = 0; b < 32 && (x >> 31) == 0; b++) {
- x <<= 1;
- }
- return b;
- }
- #endif /* use_builtin(__builtin_clz) */
- #if use_builtin(__builtin_clzll)
- static inline int u64_count_leading_zeros(uint64_t x)
- {
- return x == 0 ? 64 : __builtin_clzll(x);
- }
- #else /* !use_builtin(__builtin_clzll) */
- static inline int u64_count_leading_zeros(uint64_t x)
- {
- if (x == (uint32_t)x) {
- return 32 + u32_count_leading_zeros((uint32_t)x);
- } else {
- return u32_count_leading_zeros(x >> 32);
- }
- }
- #endif /* use_builtin(__builtin_clzll) */
- #if use_builtin(__builtin_ctz)
- static inline int u32_count_trailing_zeros(uint32_t x)
- {
- return x == 0 ? 32 : __builtin_ctz(x);
- }
- #else /* !use_builtin(__builtin_ctz) */
- static inline int u32_count_trailing_zeros(uint32_t x)
- {
- int b;
- for (b = 0; b < 32 && (x & 1) == 0; b++) {
- x >>= 1;
- }
- return b;
- }
- #endif /* use_builtin(__builtin_ctz) */
- #if use_builtin(__builtin_ctzll)
- static inline int u64_count_trailing_zeros(uint64_t x)
- {
- return x == 0 ? 64 : __builtin_ctzll(x);
- }
- #else /* !use_builtin(__builtin_ctzll) */
- static inline int u64_count_trailing_zeros(uint64_t x)
- {
- if ((uint32_t)x) {
- return u32_count_trailing_zeros((uint32_t)x);
- } else {
- return 32 + u32_count_trailing_zeros(x >> 32);
- }
- }
- #endif /* use_builtin(__builtin_ctzll) */
- #undef use_builtin
|