Commit c26fc959 authored by Christopher Friedt's avatar Christopher Friedt Committed by Carles Cufi
Browse files

sys: util: Power-of-Two utilities



* remove previous `LOG2CEIL()` from `<zephyr/toolchain/common.h>`
  - `LOG2CEIL()` is not used in-tree by any C or C++ source
  - only used are in `.ld` files
  - however, it is built-in to both GNU and LLVM linkers
* sys: util: provide LOG2() and LOG2CEIL() macros
  - calculate `log(x)` and `ceil(log(x))`, respectively
  - both are compile-time `const` / ok for `constexpr`
  - also runtime efficient with `__builtin_clz()`
  - `LOG2()`, `NUMBITS()`, and `IS_POWER_OF_TWO()` macros
    contributed by @JordanYates
  - `LOG2CEIL()` contributed by @oyvindronningstad
* sys: util: provide Next-Highest Power-of-Two macro `NHPOT()`
  - calculate `next = pow(2, ceil(log(x)/log(2)))`
  - leverages `LOG2CEIL()`

Signed-off-by: default avatarChris Friedt <cfriedt@meta.com>
parent d095f7d2
Loading
Loading
Loading
Loading
+115 −2
Original line number Diff line number Diff line
@@ -26,6 +26,90 @@
#include <zephyr/types.h>
#include <stddef.h>

/** @brief Number of bits that make up a type */
#define NUM_BITS(t) (sizeof(t) * 8)

#ifdef __cplusplus
template <typename T> static inline constexpr int __z_log2_impl(T x)
{
	if (sizeof(x) < sizeof(unsigned int)) {
		return ((x) < 1) * (-1) +
		       ((x) >= 1) * (NUM_BITS(unsigned int) - __builtin_clz(x) - 1);
	} else if (sizeof(x) <= sizeof(unsigned long long)) {
		return ((x) < 1) * (-1) +
		       ((x) >= 1) * (NUM_BITS(unsigned long long) - __builtin_clzll(x) - 1);
	}

	/* No declaration of __ASSERT is available here */
	static_assert(sizeof(x) <= sizeof(unsigned long long), "unsupported type for LOG2()");

	return -1;
}

template <typename T> static inline constexpr int __z_log2ceil_impl(T x)
{
	if (sizeof(x) < sizeof(unsigned int)) {
		return (x > 1) * (NUM_BITS(unsigned int) - __builtin_clz(x - 1));
	} else if (sizeof(x) <= sizeof(unsigned long long)) {
		return (x > 1) * (NUM_BITS(unsigned long long) - __builtin_clzll(x - 1));
	}

	/* No declaration of __ASSERT is available here */
	static_assert(sizeof(x) <= sizeof(unsigned long long), "unsupported type for LOG2CEIL()");

	return -1;
}

template <typename T> static inline constexpr uint64_t __z_nhpot_impl(T x)
{
	int l2c = __z_log2ceil_impl(x);

	return (l2c != NUM_BITS(unsigned long long)) * (1ULL << l2c);
}
#else
#define __z_log2_impl(x)                                                                           \
	(((x) < 1) * (-1) +                                                                        \
	 ((x) >= 1) * _Generic((x), char                                                           \
			       : (NUM_BITS(unsigned int) - __builtin_clz(x) - 1), unsigned char    \
			       : (NUM_BITS(unsigned int) - __builtin_clz(x) - 1), short            \
			       : (NUM_BITS(unsigned int) - __builtin_clz(x) - 1), unsigned short   \
			       : (NUM_BITS(unsigned int) - __builtin_clz(x) - 1), int              \
			       : (NUM_BITS(unsigned int) - __builtin_clz(x) - 1), unsigned int     \
			       : (NUM_BITS(unsigned int) - __builtin_clz(x) - 1), long             \
			       : (NUM_BITS(unsigned long) - __builtin_clzl(x) - 1), unsigned long  \
			       : (NUM_BITS(unsigned long) - __builtin_clzl(x) - 1), long long      \
			       : (NUM_BITS(unsigned long long) - __builtin_clzll(x) - 1),          \
				 unsigned long long                                                \
			       : (NUM_BITS(unsigned long long) - __builtin_clzll(x) - 1)))

#define __z_log2ceil_impl(x)                                                                       \
	(((x) > 1) *                                                                               \
	 _Generic((x), char                                                                        \
		  : (NUM_BITS(unsigned int) - __builtin_clz((x)-1)), unsigned char                 \
		  : (NUM_BITS(unsigned int) - __builtin_clz((x)-1)), short                         \
		  : (NUM_BITS(unsigned int) - __builtin_clz((x)-1)), unsigned short                \
		  : (NUM_BITS(unsigned int) - __builtin_clz((x)-1)), int                           \
		  : (NUM_BITS(unsigned int) - __builtin_clz((x)-1)), unsigned int                  \
		  : (NUM_BITS(unsigned int) - __builtin_clz((x)-1)), long                          \
		  : (NUM_BITS(unsigned long) - __builtin_clzl((x)-1)), unsigned long               \
		  : (NUM_BITS(unsigned long) - __builtin_clzl((x)-1)), long long                   \
		  : (NUM_BITS(unsigned long long) - __builtin_clzll((x)-1)), unsigned long long    \
		  : (NUM_BITS(unsigned long long) - __builtin_clzll((x)-1))))

#define __z_nhpot_impl(x)                                                              \
	_Generic((x), char                                                             \
					     : (1UL << LOG2CEIL(x)), unsigned char                 \
					     : (1UL << LOG2CEIL(x)), short                         \
					     : (1UL << LOG2CEIL(x)), unsigned short                \
					     : (1ULL << LOG2CEIL(x)), int                          \
					     : (1ULL << LOG2CEIL(x)), unsigned int                 \
					     : (1ULL << LOG2CEIL(x)), long                         \
					     : (1ULL << LOG2CEIL(x)), unsigned long                \
					     : (1ULL << LOG2CEIL(x)), long long                    \
					     : (1ULL << LOG2CEIL(x)), unsigned long long           \
					     : (1ULL << LOG2CEIL(x)))
#endif

#ifdef __cplusplus
extern "C" {
#endif
@@ -314,7 +398,7 @@ extern "C" {
 */
static inline bool is_power_of_two(unsigned int x)
{
	return (x != 0U) && ((x & (x - 1U)) == 0U);
	return IS_POWER_OF_TWO(x);
}

/**
@@ -508,6 +592,35 @@ char *utf8_trunc(char *utf8_str);
 */
char *utf8_lcpy(char *dst, const char *src, size_t n);

/**
 * @brief Compute log2(x)
 *
 * @param x An unsigned integral value
 *
 * @param x value to compute logarithm of (positive only)
 *
 * @return log2(x) when 1 <= x <= max(x), -1 when x < 1
 */
#define LOG2(x) __z_log2_impl(x)

/**
 * @brief Compute ceil(log2(x))
 *
 * @param x An unsigned integral value
 *
 * @return ceil(log2(x)) when 1 <= x <= max(type(x)), 0 when x < 1
 */
#define LOG2CEIL(x) __z_log2ceil_impl(x)

/**
 * @brief Compute 2^ceil(log2(x))
 *
 * @param x An unsigned integral value
 *
 * @return 2^ceil(log2(x)) or 0 if 2^ceil(log2(x)) would saturate 64-bits
 */
#define NHPOT(x) __z_nhpot_impl(x)

#ifdef __cplusplus
}
#endif
+3 −0
Original line number Diff line number Diff line
@@ -73,6 +73,9 @@ extern "C" {
 */
#define BIT64_MASK(n) (BIT64(n) - 1ULL)

/** @brief Check if a @p x is a power of two */
#define IS_POWER_OF_TWO(x) (((x) != 0U) && (((x) & ((x) - 1U)) == 0U))

/**
 * @brief Check if bits are set continuously from the specified bit
 *
+0 −15
Original line number Diff line number Diff line
@@ -291,19 +291,4 @@
	static const void * const symbol##_ptr  __used \
	__attribute__((__section__(".symbol_to_keep"))) = (void *)&symbol

#define LOG2CEIL(x) \
	((((x) <= 4) ? 2 : (((x) <= 8) ? 3 : (((x) <= 16) ? \
	4 : (((x) <= 32) ? 5 : (((x) <= 64) ? 6 : (((x) <= 128) ? \
	7 : (((x) <= 256) ? 8 : (((x) <= 512) ? 9 : (((x) <= 1024) ? \
	10 : (((x) <= 2048) ? 11 : (((x) <= 4096) ? 12 : (((x) <= 8192) ? \
	13 : (((x) <= 16384) ? 14 : (((x) <= 32768) ? 15:(((x) <= 65536) ? \
	16 : (((x) <= 131072) ? 17 : (((x) <= 262144) ? 18:(((x) <= 524288) ? \
	19 : (((x) <= 1048576) ? 20 : (((x) <= 2097152) ? \
	21 : (((x) <= 4194304) ? 22 : (((x) <= 8388608) ? \
	23 : (((x) <= 16777216) ? 24 : (((x) <= 33554432) ? \
	25 : (((x) <= 67108864) ? 26 : (((x) <= 134217728) ? \
	27 : (((x) <= 268435456) ? 28 : (((x) <= 536870912) ? \
	29 : (((x) <= 1073741824) ? 30 : (((x) <= 2147483648) ? \
	31 : 32)))))))))))))))))))))))))))))))

#endif /* ZEPHYR_INCLUDE_TOOLCHAIN_COMMON_H_ */