]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
add isc/bit.h and unify common bit operations
authorAydın Mercan <aydin@isc.org>
Mon, 18 Aug 2025 11:07:38 +0000 (14:07 +0300)
committerAydın Mercan <aydin@isc.org>
Tue, 19 Aug 2025 08:52:12 +0000 (11:52 +0300)
The `<isc/bit.h>` header is a GNU C11 compatible version of C23's
`<stdbit.h>`.

It currently uses either `<stdbit.h>` or the equivilent compiler
builtins. However, the generic `__builtin_ctzg` and `__builtin_ctlz`
builtins are not available in every compiler version and thus falls
back to manually selecting from type.

Furthermore, the ctz fallback has been removed since `__builtin_ctzll`
has been used for a while directly without any compilation issues from
users. Thus, we can also require `__builtin_ctz`.

Unlike the rest of C23's bit utilities, we avoid the stdc_rotate_*
functions since we don't need the rotation modulus precision. This adds
a couple (admittedly cheap) unwanted instructions on some architectures.

lib/dns/qp.c
lib/dns/qp_p.h
lib/dns/rpz.c
lib/isc/histo.c
lib/isc/include/isc/bit.h [new file with mode: 0644]
lib/isc/include/isc/fxhash.h
lib/isc/include/isc/siphash.h
meson.build
util/checklibs.sh

index d5b22c06466aed3435af08bae10cada4a9e29fd6..3f10bd04a4bd8e0dfd35b547526e73f961cefc8c 100644 (file)
@@ -27,6 +27,7 @@
 #endif
 
 #include <isc/atomic.h>
+#include <isc/bit.h>
 #include <isc/buffer.h>
 #include <isc/log.h>
 #include <isc/magic.h>
@@ -493,11 +494,11 @@ cells_immutable(dns_qp_t *qp, dns_qpref_t ref) {
 static dns_qpcell_t
 next_capacity(uint32_t prev_capacity, uint32_t size) {
        /*
-        * Unfortunately builtin_clz is undefined for 0. We work around this
-        * issue by flooring the request size at 2.
+        * Request size was floored at 2 because builtin_clz used to be 0.
+        * We keep this behavior because ISC_LEADING_ZEROS(0) = 32.
         */
-       size = ISC_MAX3(size, prev_capacity, 2u);
-       uint32_t log2 = 32u - __builtin_clz(size - 1u);
+       size = ISC_MAX3(size, prev_capacity, 2U);
+       uint32_t log2 = 32U - ISC_LEADING_ZEROS(size - 1U);
 
        return 1U << ISC_CLAMP(log2, QP_CHUNK_LOG_MIN, QP_CHUNK_LOG_MAX);
 }
index efe5c5d9b008dc51e55dea3b7bb1c19d1d2509d5..a5dce44d84157a512cc17fadeec0b0e0444a4811 100644 (file)
@@ -19,6 +19,7 @@
 
 #pragma once
 
+#include <isc/bit.h>
 #include <isc/refcount.h>
 
 #include <dns/qp.h>
@@ -752,7 +753,7 @@ static inline dns_qpweight_t
 branch_count_bitmap_before(dns_qpnode_t *n, dns_qpshift_t bit) {
        uint64_t mask = (1ULL << bit) - 1 - TAG_MASK;
        uint64_t bitmap = branch_index(n) & mask;
-       return (dns_qpweight_t)__builtin_popcountll(bitmap);
+       return (dns_qpweight_t)ISC_POPCOUNT(bitmap);
 }
 
 /*
index 88ca72de29f02b2f1e458ffdd9a69c3a30c96800..3333478e20af9b05f8caba11c4ad21503cbe3f09 100644 (file)
@@ -19,6 +19,7 @@
 #include <stdlib.h>
 
 #include <isc/async.h>
+#include <isc/bit.h>
 #include <isc/buffer.h>
 #include <isc/log.h>
 #include <isc/loop.h>
@@ -1074,45 +1075,6 @@ name2data(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type,
        (void)dns_name_concatenate(&tmp_name, dns_rootname, trig_name);
 }
 
-#ifndef HAVE_BUILTIN_CLZ
-/**
- * \brief Count Leading Zeros: Find the location of the left-most set
- * bit.
- */
-static unsigned int
-clz(dns_rpz_cidr_word_t w) {
-       unsigned int bit;
-
-       bit = DNS_RPZ_CIDR_WORD_BITS - 1;
-
-       if ((w & 0xffff0000) != 0) {
-               w >>= 16;
-               bit -= 16;
-       }
-
-       if ((w & 0xff00) != 0) {
-               w >>= 8;
-               bit -= 8;
-       }
-
-       if ((w & 0xf0) != 0) {
-               w >>= 4;
-               bit -= 4;
-       }
-
-       if ((w & 0xc) != 0) {
-               w >>= 2;
-               bit -= 2;
-       }
-
-       if ((w & 2) != 0) {
-               --bit;
-       }
-
-       return bit;
-}
-#endif /* ifndef HAVE_BUILTIN_CLZ */
-
 /*
  * Find the first differing bit in two keys (IP addresses).
  */
@@ -1132,11 +1094,7 @@ diff_keys(const dns_rpz_cidr_key_t *key1, dns_rpz_prefix_t prefix1,
        for (i = 0; bit < maxbit; i++, bit += DNS_RPZ_CIDR_WORD_BITS) {
                delta = key1->w[i] ^ key2->w[i];
                if (delta != 0) {
-#ifdef HAVE_BUILTIN_CLZ
-                       bit += __builtin_clz(delta);
-#else  /* ifdef HAVE_BUILTIN_CLZ */
-                       bit += clz(delta);
-#endif /* ifdef HAVE_BUILTIN_CLZ */
+                       bit += ISC_LEADING_ZEROS(delta);
                        break;
                }
        }
index 4fa9d833963971bdf3f589231b4b5557b51c9ed1..25febb55c9741e13490e5adadf3a118cba4d08bf 100644 (file)
@@ -21,6 +21,7 @@
 #include <string.h>
 
 #include <isc/atomic.h>
+#include <isc/bit.h>
 #include <isc/histo.h>
 #include <isc/magic.h>
 #include <isc/mem.h>
@@ -180,7 +181,7 @@ static inline uint
 value_to_key(const isc_histo_t *hg, uint64_t value) {
        /* ensure that denormal numbers are all in chunk zero */
        uint64_t chunked = value | CHUNKSIZE(hg);
-       int clz = __builtin_clzll((unsigned long long)(chunked));
+       int clz = ISC_LEADING_ZEROS(chunked);
        /* actually 1 less than the exponent except for denormals */
        uint exponent = 63 - hg->sigbits - clz;
        /* mantissa has leading bit set except for denormals */
diff --git a/lib/isc/include/isc/bit.h b/lib/isc/include/isc/bit.h
new file mode 100644 (file)
index 0000000..78a72df
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <limits.h>
+#include <stdint.h>
+
+#include <isc/attributes.h>
+#include <isc/util.h>
+
+#ifndef __has_header
+#define __has_header(x) 0
+#endif
+
+#if __has_header(<stdbit.h>)
+
+#include <stdbit.h>
+
+#define ISC_POPCOUNT(x)              stdc_count_zeros(x)
+#define ISC_LEADING_ZEROS(x)  stdc_leading_zeros(x)
+#define ISC_TRAILING_ZEROS(x) stdc_trailing_zeros(x)
+#define ISC_LEADING_ONES(x)   stdc_leading_ones(x)
+#define ISC_TRAILING_ONES(x)  stdc_trailing_ones(x)
+
+#else /* __has_header(<stdbit.h>) */
+
+#ifdef HAVE_BUILTIN_POPCOUNTG
+#define ISC_POPCOUNT(x) __builtin_popcountg(x)
+#else /* HAVE_BUILTIN_POPCOUNTG */
+#define ISC_POPCOUNT(x)                             \
+       _Generic((x),                               \
+               unsigned int: __builtin_popcount,   \
+               unsigned long: __builtin_popcountl, \
+               unsigned long long: __builtin_popcountll)(x)
+#endif /* HAVE_BUILTIN_POPCOUNTG */
+
+#ifdef HAVE_BUILTIN_CLZG
+#define ISC_LEADING_ZEROS(x) __builtin_clzg(x, (int)(sizeof(x) * 8))
+#else /* HAVE_BUILTIN_CLZG */
+#define ISC_LEADING_ZEROS(x)                           \
+       ((x) == 0) ? (sizeof(x) * 8)                   \
+                  : _Generic((x),                     \
+                       unsigned int: __builtin_clz,   \
+                       unsigned long: __builtin_clzl, \
+                       unsigned long long: __builtin_clzll)(x)
+#endif /* HAVE_BUILTIN_CLZG */
+
+#ifdef HAVE_BUILTIN_CTZG
+#define ISC_TRAILING_ZEROS(x) __builtin_ctzg(x, (int)sizeof(x) * 8)
+#else /* HAVE_BUILTIN_CTZG */
+#define ISC_TRAILING_ZEROS(x)                          \
+       ((x) == 0) ? (sizeof(x) * 8)                   \
+                  : _Generic((x),                     \
+                       unsigned int: __builtin_ctz,   \
+                       unsigned long: __builtin_ctzl, \
+                       unsigned long long: __builtin_ctzll)(x)
+#endif /* HAVE_BUILTIN_CTZG */
+
+#define ISC_LEADING_ONES(x)  ISC_LEADING_ZEROS(~(x))
+#define ISC_TRAILING_ONES(x) ISC_TRAILING_ZEROS(~(x))
+
+#endif /* __has_header(<stdbit.h>) */
+
+#if SIZE_MAX == UINT64_MAX
+#define isc_rotate_leftsize(x, n)  isc_rotate_left64(x, n)
+#define isc_rotate_rightsize(x, n) isc_rotate_right64(x, n)
+#elif SIZE_MAX == UINT32_MAX
+#define isc_rotate_leftsize(x, n)  isc_rotate_left32(x, n)
+#define isc_rotate_rightsize(x, n) isc_rotate_right32(x, n)
+#else
+#error "size_t must be either 32 or 64-bits"
+#endif
+
+static inline uint8_t __attribute__((always_inline))
+isc_rotate_left8(const uint8_t x, uint32_t n) {
+       return (x << n) | (x >> (8 - n));
+}
+
+static inline uint16_t __attribute__((always_inline))
+isc_rotate_left16(const uint16_t x, uint32_t n) {
+       return (x << n) | (x >> (16 - n));
+}
+
+static inline uint32_t __attribute__((always_inline))
+isc_rotate_left32(const uint32_t x, uint32_t n) {
+       return (x << n) | (x >> (32 - n));
+}
+
+static inline uint64_t __attribute__((always_inline))
+isc_rotate_left64(const uint64_t x, uint32_t n) {
+       return (x << n) | (x >> (64 - n));
+}
+
+static inline uint8_t __attribute__((always_inline))
+isc_rotate_right8(const uint8_t x, uint32_t n) {
+       return (x >> n) | (x << (8 - n));
+}
+
+static inline uint16_t __attribute__((always_inline))
+isc_rotate_right16(const uint16_t x, uint32_t n) {
+       return (x >> n) | (x << (16 - n));
+}
+
+static inline uint32_t __attribute__((always_inline))
+isc_rotate_right32(const uint32_t x, uint32_t n) {
+       return (x >> n) | (x << (32 - n));
+}
+
+static inline uint64_t __attribute__((always_inline))
+isc_rotate_right64(const uint64_t x, uint32_t n) {
+       return (x >> n) | (x << (64 - n));
+}
index b76cce4305cffe15647b69939d1f2281ce394dcd..0ee1cb0b0371305e6f5eaa150c9d75574cec70d9 100644 (file)
 #include <stdint.h>
 #include <string.h>
 
+#include <isc/bit.h>
+
 /* The constant K from Rust's fxhash */
 #define K 0x9e3779b97f4a7c15ull
 
-static inline size_t
-rotate_left(size_t x, unsigned int n) {
-       return (x << n) | (x >> (sizeof(size_t) * 8 - n));
-}
-
 static inline size_t
 fx_add_to_hash(size_t hash, size_t i) {
-       return rotate_left(hash, 5) ^ i * K;
+       return isc_rotate_leftsize(hash, 5) ^ i * K;
 }
 
 /*
index c066a85f163843914c1baeae3ac2d2985eb19796..81cfaf30c33f4f765891609eef2f2165352da358 100644 (file)
@@ -30,6 +30,7 @@
 #pragma once
 
 #include <isc/ascii.h>
+#include <isc/bit.h>
 #include <isc/endian.h>
 #include <isc/types.h>
 #include <isc/util.h>
 #define cROUNDS 2
 #define dROUNDS 4
 
-#define ROTATE64(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
-
-#define HALF_ROUND64(a, b, c, d, s, t) \
-       a += b;                        \
-       c += d;                        \
-       b = ROTATE64(b, s) ^ a;        \
-       d = ROTATE64(d, t) ^ c;        \
-       a = ROTATE64(a, 32);
+#define HALF_ROUND64(a, b, c, d, s, t)   \
+       a += b;                          \
+       c += d;                          \
+       b = isc_rotate_left64(b, s) ^ a; \
+       d = isc_rotate_left64(d, t) ^ c; \
+       a = isc_rotate_left64(a, 32);
 
 #define FULL_ROUND64(v0, v1, v2, v3)          \
        HALF_ROUND64(v0, v1, v2, v3, 13, 16); \
 
 #define SIPROUND FULL_ROUND64
 
-#define ROTATE32(x, b) (uint32_t)(((x) << (b)) | ((x) >> (32 - (b))))
-
-#define HALF_ROUND32(a, b, c, d, s, t) \
-       a += b;                        \
-       c += d;                        \
-       b = ROTATE32(b, s) ^ a;        \
-       d = ROTATE32(d, t) ^ c;        \
-       a = ROTATE32(a, 16);
+#define HALF_ROUND32(a, b, c, d, s, t)   \
+       a += b;                          \
+       c += d;                          \
+       b = isc_rotate_left32(b, s) ^ a; \
+       d = isc_rotate_left32(d, t) ^ c; \
+       a = isc_rotate_left32(a, 16);
 
 #define FULL_ROUND32(v0, v1, v2, v3)        \
        HALF_ROUND32(v0, v1, v2, v3, 5, 8); \
index 6f45d81f6335a996adf0bb1dd0d353cdba54fcc5..b4a4ca75d70cf8d6c64ec656cf23b47a6158c19a 100644 (file)
@@ -312,7 +312,6 @@ endif
 
 foreach fn : [
     '__builtin_add_overflow',
-    '__builtin_clz',
     '__builtin_mul_overflow',
     '__builtin_sub_overflow',
     '__builtin_unreachable',
@@ -322,6 +321,45 @@ foreach fn : [
     endif
 endforeach
 
+# meson_version (>=1.3.0) : required in cc.has_function
+if cc.has_function('__builtin_clzg')
+    config.set('HAVE_BUILTIN_CLZG', true)
+elif not (
+    cc.has_function('__builtin_clz')
+    and cc.has_function('__builtin_clzl')
+    and cc.has_function('__builtin_clzll')
+)
+    error(
+        '__builtin_clzg or __builtin_clz* functions are required, please fix your toolchain',
+    )
+endif
+
+# meson_version (>=1.3.0) : required in cc.has_function
+if cc.has_function('__builtin_ctzg')
+    config.set('HAVE_BUILTIN_CTZG', true)
+elif not (
+    cc.has_function('__builtin_ctz')
+    and cc.has_function('__builtin_ctzl')
+    and cc.has_function('__builtin_ctzll')
+)
+    error(
+        '__builtin_ctzg or __builtin_ctz* functions are required, please fix your toolchain',
+    )
+endif
+
+# meson_version (>=1.3.0) : required in cc.has_function
+if cc.has_function('__builtin_popcountg')
+    config.set('HAVE_BUILTIN_POPCOUNTG', true)
+elif not (
+    cc.has_function('__builtin_popcount')
+    and cc.has_function('__builtin_popcountl')
+    and cc.has_function('__builtin_popcountll')
+)
+    error(
+        '__builtin_popcountg or __builtin_popcount* functions are required, please fix your toolchain',
+    )
+endif
+
 foreach attr : ['malloc', 'returns_nonnull']
     if cc.has_function_attribute(attr)
         config.set('HAVE_FUNC_ATTRIBUTE_@0@'.format(attr.to_upper()), 1)
index e0f8c79d641b9df1fa721c870ed70425ce3c46c7..b47cb52fe1f4cc6fbc6190b5ddc9bfc8beebea9a 100755 (executable)
@@ -64,4 +64,15 @@ list=$(git grep -l memory_order_.* lib bin ':(exclude)lib/isc/include/isc/atomic
   echo "$list"
 }
 
+#
+# Check for the usage of built-in bit operarators
+#
+list=$(git grep -l -e '__builtin_ctz' --or -e '__builtin_popcount' --or -e '__builtin_clz' lib bin ':(exclude)lib/isc/include/isc/bit\.h' \
+  | grep -e '\.c$' -e '\.h$')
+[ -n "$list" ] && {
+  status=1
+  echo 'Prefer the helpers in <isc/bit.h> over builtin bit utilities:'
+  echo "$list"
+}
+
 exit $status