2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-22 01:59:26 +00:00

add isc/bit.h and unify common bit operations

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.
This commit is contained in:
Aydın Mercan 2025-08-18 14:07:38 +03:00
parent 0da10d8bbe
commit 7d5928c3a2
No known key found for this signature in database
9 changed files with 200 additions and 73 deletions

View File

@ -27,6 +27,7 @@
#endif #endif
#include <isc/atomic.h> #include <isc/atomic.h>
#include <isc/bit.h>
#include <isc/buffer.h> #include <isc/buffer.h>
#include <isc/log.h> #include <isc/log.h>
#include <isc/magic.h> #include <isc/magic.h>
@ -493,11 +494,11 @@ cells_immutable(dns_qp_t *qp, dns_qpref_t ref) {
static dns_qpcell_t static dns_qpcell_t
next_capacity(uint32_t prev_capacity, uint32_t size) { next_capacity(uint32_t prev_capacity, uint32_t size) {
/* /*
* Unfortunately builtin_clz is undefined for 0. We work around this * Request size was floored at 2 because builtin_clz used to be 0.
* issue by flooring the request size at 2. * We keep this behavior because ISC_LEADING_ZEROS(0) = 32.
*/ */
size = ISC_MAX3(size, prev_capacity, 2u); size = ISC_MAX3(size, prev_capacity, 2U);
uint32_t log2 = 32u - __builtin_clz(size - 1u); uint32_t log2 = 32U - ISC_LEADING_ZEROS(size - 1U);
return 1U << ISC_CLAMP(log2, QP_CHUNK_LOG_MIN, QP_CHUNK_LOG_MAX); return 1U << ISC_CLAMP(log2, QP_CHUNK_LOG_MIN, QP_CHUNK_LOG_MAX);
} }

View File

@ -19,6 +19,7 @@
#pragma once #pragma once
#include <isc/bit.h>
#include <isc/refcount.h> #include <isc/refcount.h>
#include <dns/qp.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) { branch_count_bitmap_before(dns_qpnode_t *n, dns_qpshift_t bit) {
uint64_t mask = (1ULL << bit) - 1 - TAG_MASK; uint64_t mask = (1ULL << bit) - 1 - TAG_MASK;
uint64_t bitmap = branch_index(n) & mask; uint64_t bitmap = branch_index(n) & mask;
return (dns_qpweight_t)__builtin_popcountll(bitmap); return (dns_qpweight_t)ISC_POPCOUNT(bitmap);
} }
/* /*

View File

@ -19,6 +19,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <isc/async.h> #include <isc/async.h>
#include <isc/bit.h>
#include <isc/buffer.h> #include <isc/buffer.h>
#include <isc/log.h> #include <isc/log.h>
#include <isc/loop.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); (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). * 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) { for (i = 0; bit < maxbit; i++, bit += DNS_RPZ_CIDR_WORD_BITS) {
delta = key1->w[i] ^ key2->w[i]; delta = key1->w[i] ^ key2->w[i];
if (delta != 0) { if (delta != 0) {
#ifdef HAVE_BUILTIN_CLZ bit += ISC_LEADING_ZEROS(delta);
bit += __builtin_clz(delta);
#else /* ifdef HAVE_BUILTIN_CLZ */
bit += clz(delta);
#endif /* ifdef HAVE_BUILTIN_CLZ */
break; break;
} }
} }

View File

@ -21,6 +21,7 @@
#include <string.h> #include <string.h>
#include <isc/atomic.h> #include <isc/atomic.h>
#include <isc/bit.h>
#include <isc/histo.h> #include <isc/histo.h>
#include <isc/magic.h> #include <isc/magic.h>
#include <isc/mem.h> #include <isc/mem.h>
@ -180,7 +181,7 @@ static inline uint
value_to_key(const isc_histo_t *hg, uint64_t value) { value_to_key(const isc_histo_t *hg, uint64_t value) {
/* ensure that denormal numbers are all in chunk zero */ /* ensure that denormal numbers are all in chunk zero */
uint64_t chunked = value | CHUNKSIZE(hg); 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 */ /* actually 1 less than the exponent except for denormals */
uint exponent = 63 - hg->sigbits - clz; uint exponent = 63 - hg->sigbits - clz;
/* mantissa has leading bit set except for denormals */ /* mantissa has leading bit set except for denormals */

123
lib/isc/include/isc/bit.h Normal file
View File

@ -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));
}

View File

@ -29,17 +29,14 @@
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <isc/bit.h>
/* The constant K from Rust's fxhash */ /* The constant K from Rust's fxhash */
#define K 0x9e3779b97f4a7c15ull #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 static inline size_t
fx_add_to_hash(size_t hash, size_t i) { 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;
} }
/* /*

View File

@ -30,6 +30,7 @@
#pragma once #pragma once
#include <isc/ascii.h> #include <isc/ascii.h>
#include <isc/bit.h>
#include <isc/endian.h> #include <isc/endian.h>
#include <isc/types.h> #include <isc/types.h>
#include <isc/util.h> #include <isc/util.h>
@ -43,14 +44,12 @@
#define cROUNDS 2 #define cROUNDS 2
#define dROUNDS 4 #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; \
#define HALF_ROUND64(a, b, c, d, s, t) \ c += d; \
a += b; \ b = isc_rotate_left64(b, s) ^ a; \
c += d; \ d = isc_rotate_left64(d, t) ^ c; \
b = ROTATE64(b, s) ^ a; \ a = isc_rotate_left64(a, 32);
d = ROTATE64(d, t) ^ c; \
a = ROTATE64(a, 32);
#define FULL_ROUND64(v0, v1, v2, v3) \ #define FULL_ROUND64(v0, v1, v2, v3) \
HALF_ROUND64(v0, v1, v2, v3, 13, 16); \ HALF_ROUND64(v0, v1, v2, v3, 13, 16); \
@ -58,14 +57,12 @@
#define SIPROUND FULL_ROUND64 #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; \
#define HALF_ROUND32(a, b, c, d, s, t) \ c += d; \
a += b; \ b = isc_rotate_left32(b, s) ^ a; \
c += d; \ d = isc_rotate_left32(d, t) ^ c; \
b = ROTATE32(b, s) ^ a; \ a = isc_rotate_left32(a, 16);
d = ROTATE32(d, t) ^ c; \
a = ROTATE32(a, 16);
#define FULL_ROUND32(v0, v1, v2, v3) \ #define FULL_ROUND32(v0, v1, v2, v3) \
HALF_ROUND32(v0, v1, v2, v3, 5, 8); \ HALF_ROUND32(v0, v1, v2, v3, 5, 8); \

View File

@ -312,7 +312,6 @@ endif
foreach fn : [ foreach fn : [
'__builtin_add_overflow', '__builtin_add_overflow',
'__builtin_clz',
'__builtin_mul_overflow', '__builtin_mul_overflow',
'__builtin_sub_overflow', '__builtin_sub_overflow',
'__builtin_unreachable', '__builtin_unreachable',
@ -322,6 +321,45 @@ foreach fn : [
endif endif
endforeach 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'] foreach attr : ['malloc', 'returns_nonnull']
if cc.has_function_attribute(attr) if cc.has_function_attribute(attr)
config.set('HAVE_FUNC_ATTRIBUTE_@0@'.format(attr.to_upper()), 1) config.set('HAVE_FUNC_ATTRIBUTE_@0@'.format(attr.to_upper()), 1)

View File

@ -64,4 +64,15 @@ list=$(git grep -l memory_order_.* lib bin ':(exclude)lib/isc/include/isc/atomic
echo "$list" 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 exit $status