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:
parent
0da10d8bbe
commit
7d5928c3a2
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
123
lib/isc/include/isc/bit.h
Normal 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));
|
||||||
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -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); \
|
||||||
|
40
meson.build
40
meson.build
@ -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)
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user