Zephyr API Documentation 4.4.99
A Scalable Open Source RTOS
Loading...
Searching...
No Matches
math_extras_impl.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2019 Facebook.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
11
12#ifndef ZEPHYR_INCLUDE_SYS_MATH_EXTRAS_H_
13#error "please include <sys/math_extras.h> instead of this file"
14#endif
15
16#include <zephyr/toolchain.h>
17
19
20/*
21 * Force the use of portable C code (no builtins) by defining
22 * PORTABLE_MISC_MATH_EXTRAS before including <misc/math_extras.h>.
23 * This is primarily for use by tests.
24 *
25 * We'll #undef use_builtin again at the end of the file.
26 */
27#ifdef PORTABLE_MISC_MATH_EXTRAS
28#define use_builtin(x) 0
29#define __has_type_128 0
30#else
31#define use_builtin(x) HAS_BUILTIN(x)
32#ifdef __SIZEOF_INT128__
33 #define __has_type_128 1
34#else
35 #define __has_type_128 0
36#endif
37#endif
38
39#if use_builtin(__builtin_add_overflow)
40static inline bool u16_add_overflow(uint16_t a, uint16_t b, uint16_t *result)
41{
42 return __builtin_add_overflow(a, b, result);
43}
44
45static inline bool u32_add_overflow(uint32_t a, uint32_t b, uint32_t *result)
46{
47 return __builtin_add_overflow(a, b, result);
48}
49
50static inline bool u64_add_overflow(uint64_t a, uint64_t b, uint64_t *result)
51{
52 return __builtin_add_overflow(a, b, result);
53}
54
55static inline bool size_add_overflow(size_t a, size_t b, size_t *result)
56{
57 return __builtin_add_overflow(a, b, result);
58}
59#else /* !use_builtin(__builtin_add_overflow) */
60static inline bool u16_add_overflow(uint16_t a, uint16_t b, uint16_t *result)
61{
62 uint16_t c = a + b;
63
64 *result = c;
65
66 return c < a;
67}
68
69static inline bool u32_add_overflow(uint32_t a, uint32_t b, uint32_t *result)
70{
71 uint32_t c = a + b;
72
73 *result = c;
74
75 return c < a;
76}
77
78static inline bool u64_add_overflow(uint64_t a, uint64_t b, uint64_t *result)
79{
80 uint64_t c = a + b;
81
82 *result = c;
83
84 return c < a;
85}
86
87static inline bool size_add_overflow(size_t a, size_t b, size_t *result)
88{
89 size_t c = a + b;
90
91 *result = c;
92
93 return c < a;
94}
95#endif /* use_builtin(__builtin_add_overflow) */
96
97#if use_builtin(__builtin_mul_overflow)
98static inline bool u16_mul_overflow(uint16_t a, uint16_t b, uint16_t *result)
99{
100 return __builtin_mul_overflow(a, b, result);
101}
102
103static inline bool u32_mul_overflow(uint32_t a, uint32_t b, uint32_t *result)
104{
105 return __builtin_mul_overflow(a, b, result);
106}
107
108static inline bool u64_mul_overflow(uint64_t a, uint64_t b, uint64_t *result)
109{
110 return __builtin_mul_overflow(a, b, result);
111}
112
113static inline bool size_mul_overflow(size_t a, size_t b, size_t *result)
114{
115 return __builtin_mul_overflow(a, b, result);
116}
117#else /* !use_builtin(__builtin_mul_overflow) */
118static inline bool u16_mul_overflow(uint16_t a, uint16_t b, uint16_t *result)
119{
120 uint16_t c = a * b;
121
122 *result = c;
123
124 return a != 0 && (c / a) != b;
125}
126
127static inline bool u32_mul_overflow(uint32_t a, uint32_t b, uint32_t *result)
128{
129 uint32_t c = a * b;
130
131 *result = c;
132
133 return a != 0 && (c / a) != b;
134}
135
136static inline bool u64_mul_overflow(uint64_t a, uint64_t b, uint64_t *result)
137{
138 uint64_t c = a * b;
139
140 *result = c;
141
142 return a != 0 && (c / a) != b;
143}
144
145static inline bool size_mul_overflow(size_t a, size_t b, size_t *result)
146{
147 size_t c = a * b;
148
149 *result = c;
150
151 return a != 0 && (c / a) != b;
152}
153#endif /* use_builtin(__builtin_mul_overflow) */
154
155
156/*
157 * The GCC builtins __builtin_clz(), __builtin_ctz(), and 64-bit
158 * variants are described by the GCC documentation as having undefined
159 * behavior when the argument is zero. See
160 * https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html.
161 *
162 * The undefined behavior applies to all architectures, regardless of
163 * the behavior of the instruction used to implement the builtin.
164 *
165 * We don't want to expose users of this API to the undefined behavior,
166 * so we use a conditional to explicitly provide the correct result when
167 * x=0.
168 *
169 * Most instruction set architectures have a CLZ instruction or similar
170 * that already computes the correct result for x=0. Both GCC and Clang
171 * know this and simply generate a CLZ instruction, optimizing away the
172 * conditional.
173 *
174 * For x86, and for compilers that fail to eliminate the conditional,
175 * there is often another opportunity for optimization since code using
176 * these functions tends to contain a zero check already. For example,
177 * from kernel/sched.c:
178 *
179 * struct k_thread *z_priq_mq_best(struct _priq_mq *pq)
180 * {
181 * if (!pq->bitmask) {
182 * return NULL;
183 * }
184 *
185 * struct k_thread *thread = NULL;
186 * sys_dlist_t *l =
187 * &pq->queues[u32_count_trailing_zeros(pq->bitmask)];
188 *
189 * ...
190 *
191 * The compiler will often be able to eliminate the redundant x == 0
192 * check after inlining the call to u32_count_trailing_zeros().
193 */
194
195#if use_builtin(__builtin_clz)
196static inline int u32_count_leading_zeros(uint32_t x)
197{
198 return (x == 0) ? 32 : __builtin_clz(x);
199}
200#else /* !use_builtin(__builtin_clz) */
201static inline int u32_count_leading_zeros(uint32_t x)
202{
203 int b;
204
205 for (b = 0; b < 32 && (x >> 31) == 0; b++) {
206 x <<= 1;
207 }
208
209 return b;
210}
211#endif /* use_builtin(__builtin_clz) */
212
213#if use_builtin(__builtin_clzll)
214static inline int u64_count_leading_zeros(uint64_t x)
215{
216 return (x == 0) ? 64 : __builtin_clzll(x);
217}
218#else /* !use_builtin(__builtin_clzll) */
219static inline int u64_count_leading_zeros(uint64_t x)
220{
221 if (x == (uint32_t)x) {
222 return 32 + u32_count_leading_zeros((uint32_t)x);
223 } else {
224 return u32_count_leading_zeros(x >> 32);
225 }
226}
227#endif /* use_builtin(__builtin_clzll) */
228
229#if use_builtin(__builtin_ctz)
230static inline int u32_count_trailing_zeros(uint32_t x)
231{
232 return (x == 0) ? 32 : __builtin_ctz(x);
233}
234#else /* !use_builtin(__builtin_ctz) */
235static inline int u32_count_trailing_zeros(uint32_t x)
236{
237 int b;
238
239 for (b = 0; b < 32 && (x & 1) == 0; b++) {
240 x >>= 1;
241 }
242
243 return b;
244}
245#endif /* use_builtin(__builtin_ctz) */
246
247#if use_builtin(__builtin_ctzll)
248static inline int u64_count_trailing_zeros(uint64_t x)
249{
250 return (x == 0) ? 64 : __builtin_ctzll(x);
251}
252#else /* !use_builtin(__builtin_ctzll) */
253static inline int u64_count_trailing_zeros(uint64_t x)
254{
255 if ((uint32_t)x) {
257 } else {
258 return 32 + u32_count_trailing_zeros(x >> 32);
259 }
260}
261#endif /* use_builtin(__builtin_ctzll) */
262
273#if __has_type_128
274static inline void i128_multiply_i64_i64(int64_t a, int64_t b, int128_t *result)
275{
276 __int128 c = (__int128)a * (__int128)b;
277
278 result->low = (uint64_t)c;
279 result->high = (uint64_t)(c >> 64);
280}
281#else
282static inline void i128_multiply_i64_i64(int64_t a, int64_t b, int128_t *result)
283{
284 uint64_t u_a = (a < 0) ? (uint64_t)-a : (uint64_t)a;
285 uint64_t u_b = (b < 0) ? (uint64_t)-b : (uint64_t)b;
286 int sign = (a < 0) ^ (b < 0);
287
288 /* Split to 32-bit values */
289 uint64_t a_lo = u_a & 0xFFFFFFFFULL;
290 uint64_t a_hi = u_a >> 32;
291 uint64_t b_lo = u_b & 0xFFFFFFFFULL;
292 uint64_t b_hi = u_b >> 32;
293
294 /* Calculate product, just like in school */
295 uint64_t res_0 = a_lo * b_lo;
296 uint64_t res_1 = a_hi * b_lo;
297 uint64_t res_2 = a_lo * b_hi;
298 uint64_t res_3 = a_hi * b_hi;
299
300 /* Combine values including carry */
301 uint64_t carry = 0;
302 uint64_t middle = (res_0 >> 32) + (res_1 & 0xFFFFFFFFULL) + (res_2 & 0xFFFFFFFFULL);
303
304 result->low = (res_0 & 0xFFFFFFFFULL) | (middle << 32);
305
306 /* Move the top part */
307 carry = (middle >> 32) + (res_1 >> 32) + (res_2 >> 32) + res_3;
308 result->high = carry;
309
310 /* Calculate two-complement if sign is minus */
311 if (sign) {
312 result->low = ~result->low + 1;
313 result->high = ~result->high + (result->low == 0 ? 1 : 0);
314 }
315}
316#endif /* __has_type_128 */
317
318#undef use_builtin
319
static bool u16_mul_overflow(uint16_t a, uint16_t b, uint16_t *result)
Multiply two unsigned 16-bit integers.
static int u64_count_trailing_zeros(uint64_t x)
Count the number of trailing zero bits in a 64-bit integer.
static bool u64_mul_overflow(uint64_t a, uint64_t b, uint64_t *result)
Multiply two unsigned 64-bit integers.
static bool u32_add_overflow(uint32_t a, uint32_t b, uint32_t *result)
Add two unsigned 32-bit integers.
static bool u32_mul_overflow(uint32_t a, uint32_t b, uint32_t *result)
Multiply two unsigned 32-bit integers.
static int u32_count_trailing_zeros(uint32_t x)
Count the number of trailing zero bits in a 32-bit integer.
static bool u16_add_overflow(uint16_t a, uint16_t b, uint16_t *result)
Add two unsigned 16-bit integers.
static bool size_mul_overflow(size_t a, size_t b, size_t *result)
Multiply two size_t integers.
static bool size_add_overflow(size_t a, size_t b, size_t *result)
Add two size_t integers.
static int u32_count_leading_zeros(uint32_t x)
Count the number of leading zero bits in a 32-bit integer.
static void i128_multiply_i64_i64(int64_t a, int64_t b, int128_t *result)
Multiply two signed 64-bit integers and store the result in a 128-bit integer.
static int u64_count_leading_zeros(uint64_t x)
Count the number of leading zero bits in a 64-bit integer.
static bool u64_add_overflow(uint64_t a, uint64_t b, uint64_t *result)
Add two unsigned 64-bit integers.
__UINT32_TYPE__ uint32_t
Definition stdint.h:90
__UINT64_TYPE__ uint64_t
Definition stdint.h:91
__UINT16_TYPE__ uint16_t
Definition stdint.h:89
__INT64_TYPE__ int64_t
Definition stdint.h:75
128-bit integer structure.
Definition math_extras.h:187
uint64_t high
High-order 64 bits (includes sign bit).
Definition math_extras.h:191
uint64_t low
Low-order 64 bits.
Definition math_extras.h:189
Macros to abstract toolchain specific capabilities.