diff --git a/CMakeLists.txt b/CMakeLists.txt index 55774f891b0a575670b077c00ebb176b6bc718c3..0d2da0809acda6283f1d39fddaee95e3612fb7c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ include(CheckLibraryExists) include(CheckIncludeFile) include(CheckCCompilerFlag) include(CheckSymbolExists) +include(CheckCSourceRuns) include(CheckCXXSourceRuns) include(TestBigEndian) include(CheckFunctionExists) @@ -94,6 +95,24 @@ if (CMAKE_COMPILER_IS_GNUCC) endif() endif() +# CMake's check_function_exists doesn't check builtins +check_c_source_runs("int main(int a) { __builtin_ctz(a); return 0; }" + HAVE_CTZ) +check_c_source_runs("int main(int a) { __builtin_ctzll(a); return 0; }" + HAVE_CTZLL) +check_c_source_runs("int main(int a) { __builtin_clz(a); return 0; }" + HAVE_CLZ) +check_c_source_runs("int main(int a) { __builtin_clzll(a); return 0; }" + HAVE_CLZLL) +check_c_source_runs("int main(int a) { __builtin_popcount(a); return 0; }" + HAVE_POPCOUNT) +check_c_source_runs("int main(int a) { __builtin_popcountll(a); return 0; }" + HAVE_POPCOUNTLL) +check_c_source_runs("int main(int a) { __builtin_bswap32(a); return 0; }" + HAVE_BSWAP32) +check_c_source_runs("int main(int a) { __builtin_bswap64(a); return 0; }" + HAVE_BSWAP64) + # # Enable 'make TAGS' target. # diff --git a/include/config.h.cmake b/include/config.h.cmake index 407b4d8fc7a542980df123b1e5516b90af9725d0..b348c9504f02ea1de21f21209d2cab703ae775f5 100644 --- a/include/config.h.cmake +++ b/include/config.h.cmake @@ -74,6 +74,18 @@ */ #cmakedefine HAVE_BYTE_ORDER_BIG_ENDIAN 1 +/* + * Set if compiler has __builtin_XXX methods. + */ +#cmakedefine HAVE_CTZ 1 +#cmakedefine HAVE_CTZLL 1 +#cmakedefine HAVE_CLZ 1 +#cmakedefine HAVE_CLZLL 1 +#cmakedefine HAVE_POPCOUNT 1 +#cmakedefine HAVE_POPCOUNTLL 1 +#cmakedefine HAVE_BSWAP32 1 +#cmakedefine HAVE_BSWAP64 1 + /* * pthread have problems with -std=c99 */ diff --git a/include/lib/bit/bit.h b/include/lib/bit/bit.h new file mode 100644 index 0000000000000000000000000000000000000000..5af73a025f016ebc965cf8ea0f2acfbe8a641307 --- /dev/null +++ b/include/lib/bit/bit.h @@ -0,0 +1,295 @@ +#ifndef TARANTOOL_LIB_BIT_BIT_H_INCLUDED +#define TARANTOOL_LIB_BIT_BIT_H_INCLUDED +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/** + * @file + * @brief Bit manipulation library + */ +#include "config.h" + +#include <stddef.h> +#include <stdint.h> +#include <limits.h> + +/** + * @cond false + * @brief Naive implementation of ctz. + */ +#define CTZ_NAIVE(x, bitsize) { \ + if (x == 0) { \ + return (bitsize); \ + } \ + \ + int r = 0; \ + for (; (x & 1) == 0; r++) { \ + x >>= 1; \ + } \ + \ + return r; \ +} +/** @endcond */ + +/** + * @brief Count Trailing Zeros. + * Returns the number of trailing 0-bits in @a x, starting at the least + * significant bit position. If @a x is 0, the result is undefined. + * @param x integer + * @see __builtin_ctz() + * @return the number trailing 0-bits + */ + __attribute__((const)) inline int +bit_ctz_u32(uint32_t x) +{ +#if defined(HAVE_CTZ) + return __builtin_ctz(x); +#else /* !defined(HAVE_CTZ) */ + CTZ_NAIVE(x, sizeof(uint32_t) * CHAR_BIT); +#endif +} + +/** + * @copydoc bit_ctz_u32 + */ + __attribute__((const)) inline int +bit_ctz_u64(uint64_t x) +{ +#if defined(HAVE_CTZLL) + return __builtin_ctzll(x); +#else /* !defined(HAVE_CTZLL) */ + CTZ_NAIVE(x, sizeof(uint64_t) * CHAR_BIT); +#endif +} + +#undef CTZ_NAIVE + +/** + * @cond false + * @brief Naive implementation of clz. + */ +#define CLZ_NAIVE(x, bitsize) { \ + if (x == 0) { \ + return (bitsize); \ + } \ + \ + int r = (bitsize); \ + for (; x; r--) { \ + x >>= 1; \ + } \ + \ + return r; \ +} +/** @endcond */ + +/** + * @brief Count Leading Zeros. + * Returns the number of leading 0-bits in @a x, starting at the most + * significant bit position. If @a x is 0, the result is undefined. + * @param x integer + * @see __builtin_clz() + * @return the number of leading 0-bits + */ + __attribute__((const)) inline int +bit_clz_u32(uint32_t x) +{ +#if defined(HAVE_CLZ) + return __builtin_clz(x); +#else /* !defined(HAVE_CLZ) */ + CLZ_NAIVE(x, sizeof(uint32_t) * CHAR_BIT); +#endif +} + +/** + * @copydoc bit_clz_u32 + */ +__attribute__((const)) inline int +bit_clz_u64(uint64_t x) +{ +#if defined(HAVE_CLZLL) + return __builtin_clzll(x); +#else /* !defined(HAVE_CLZLL) */ + CLZ_NAIVE(x, sizeof(uint64_t) * CHAR_BIT); +#endif +} + +#undef CLZ_NAIVE + +/** + * @cond false + * @brief Naive implementation of popcount. + */ +#define POPCOUNT_NAIVE(x, bitsize) { \ + int r; \ + for (r = 0; x; r++) { \ + x &= (x-1); \ + } \ + \ + return r; \ +} +/** @endcond */ + +/** + * @brief Returns the number of 1-bits in @a x. + * @param x integer + * @see __builtin_popcount() + * @return the number of 1-bits in @a x + */ +__attribute__((const)) inline int +bit_count_u32(uint32_t x) +{ +#if defined(HAVE_POPCOUNT) + return __builtin_popcount(x); +#else /* !defined(HAVE_POPCOUNT) */ + POPCOUNT_NAIVE(x, sizeof(uint32_t) * CHAR_BIT); +#endif +} + +/** + * @copydoc bit_count_u32 + */ +__attribute__((const)) inline int +bit_count_u64(uint64_t x) +{ +#if defined(HAVE_POPCOUNTLL) + return __builtin_popcountll(x); +#else /* !defined(HAVE_POPCOUNTLL) */ + POPCOUNT_NAIVE(x, sizeof(uint64_t) * CHAR_BIT); +#endif +} + +#undef POPCOUNT_NAIVE + +/** + * @brief Rotate @a x left by @a r bits + * @param x integer + * @param r number for bits to rotate + * @return @a x rotated left by @a r bits + */ +__attribute__ ((const)) inline uint32_t +bit_rotl_u32(uint32_t x, int r) +{ + /* gcc recognises this code and generates a rotate instruction */ + return ((x << r) | (x >> (32 - r))); +} + +/** + * @copydoc bit_rotl_u32 + */ +__attribute__ ((const)) inline uint64_t +bit_rotl_u64(uint64_t x, int r) +{ + /* gcc recognises this code and generates a rotate instruction */ + return ((x << r) | (x >> (64 - r))); +} + +/** + * @brief Rotate @a x right by @a r bits + * @param x integer + * @param r number for bits to rotate + * @return @a x rotated right by @a r bits + * @todo Move this method to bit.h + */ +__attribute__ ((const)) inline uint32_t +bit_rotr_u32(uint32_t x, int r) +{ + /* gcc recognises this code and generates a rotate instruction */ + return ((x >> r) | (x << (32 - r))); +} + +/** + * @copydoc bit_rotr_u32 + */ +__attribute__ ((const)) inline uint64_t +bit_rotr_u64(uint64_t x, int r) +{ + /* gcc recognises this code and generates a rotate instruction */ + return ((x >> r) | (x << (64 - r))); +} + +/** + * @brief Returns a byte order swapped integer @a x. + * This function does not take into account host architecture + * (as it done by htonl / ntohl functions) and always returns @a x + * with byte order swapped (BE -> LE if @a x is in BE and vice versa). + * @param x integer + * @return @a x with swapped bytes + */ +__attribute__ ((const)) inline uint32_t +bswap_u32(uint32_t x) +{ +#if defined(HAVE_BSWAP32) + return __builtin_bswap32(x); +#else /* !defined(HAVE_BSWAP32) */ + return ((x << 24) & UINT32_C(0xff000000)) | + ((x << 8) & UINT32_C(0x00ff0000)) | + ((x >> 8) & UINT32_C(0x0000ff00)) | + ((x >> 24) & UINT32_C(0x000000ff)); +#endif +} + +/** + * @copydoc bswap_u32 + */ +__attribute__ ((const)) inline uint64_t +bswap_u64(uint64_t x) +{ +#if defined(HAVE_BSWAP64) + return __builtin_bswap64(x); +#else /* !defined(HAVE_BSWAP64) */ + return ( (x << 56) & UINT64_C(0xff00000000000000)) | + ( (x << 40) & UINT64_C(0x00ff000000000000)) | + ( (x << 24) & UINT64_C(0x0000ff0000000000)) | + ( (x << 8) & UINT64_C(0x000000ff00000000)) | + ( (x >> 8) & UINT64_C(0x00000000ff000000)) | + ( (x >> 24) & UINT64_C(0x0000000000ff0000)) | + ( (x >> 40) & UINT64_C(0x000000000000ff00)) | + ( (x >> 56) & UINT64_C(0x00000000000000ff)); +#endif +} + +/** + * @brief Index bits in the @a x, i.e. find all positions where bits are set. + * This method fills @a indexes array with found positions in increasing order. + * @a offset is added to each index before putting it into @a indexes. + * @param x integer + * @param indexes memory array where found indexes are stored + * @param offset a number added to each index + * @return pointer to last+1 element in indexes array + */ +int * +bit_index_u32(uint32_t x, int *indexes, int offset); + +/** + * @copydoc bit_index_u32 + */ +int * +bit_index_u64(uint64_t x, int *indexes, int offset); + +#endif /* TARANTOOL_LIB_BIT_BIT_H_INCLUDED */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d4dbfa486100af95f324917bda3e0c979bbab80d..6d4a02ce73c1eada33d14ed67336b59b7ad8bb74 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -143,6 +143,8 @@ endif() set (common_libraries ${common_libraries} PARENT_SCOPE) +add_subdirectory(lib) + function(tarantool_module mod) set (module_sources ${ARGN}) set(cfg_c_flags "-Wno-unused -Wno-unused-parameter") diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..e0794be0dc3402afcfe366ff8f66aaeb75560af2 --- /dev/null +++ b/src/lib/CMakeLists.txt @@ -0,0 +1,7 @@ +include_directories("${PROJECT_SOURCE_DIR}/include/lib") + +add_custom_target(libs) + +add_subdirectory(bit) +add_dependencies(libs bit) + diff --git a/src/lib/bit/CMakeLists.txt b/src/lib/bit/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..41489456e768670b324c0a74fbd916ae45d7556c --- /dev/null +++ b/src/lib/bit/CMakeLists.txt @@ -0,0 +1,3 @@ +aux_source_directory(. lib_sources) +set_source_files_compile_flags(${lib_sources}) +add_library(bit STATIC ${lib_sources}) diff --git a/src/lib/bit/bit.c b/src/lib/bit/bit.c new file mode 100644 index 0000000000000000000000000000000000000000..a7bd02655ecd0b1e02c5f1258dccccfcd2501b9a --- /dev/null +++ b/src/lib/bit/bit.c @@ -0,0 +1,144 @@ +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <lib/bit/bit.h> + +__attribute__((const)) extern inline int +bit_ctz_u32(uint32_t x); + +__attribute__((const)) extern inline int +bit_ctz_u64(uint64_t x); + +__attribute__((const)) extern inline int +bit_clz_u32(uint32_t x); + +__attribute__((const)) extern inline int +bit_clz_u64(uint64_t x); + +__attribute__((const)) extern inline int +bit_count_u32(uint32_t x); + +__attribute__((const)) extern inline int +bit_count_u64(uint64_t x); + +__attribute__ ((const)) extern inline uint32_t +bit_rotl_u32(uint32_t x, int r); + +__attribute__ ((const)) extern inline uint64_t +bit_rotl_u64(uint64_t x, int r); + +__attribute__ ((const)) extern inline uint32_t +bit_rotr_u32(uint32_t x, int r); + +__attribute__ ((const)) extern inline uint64_t +bit_rotr_u64(uint64_t x, int r); + +__attribute__ ((const)) extern inline uint32_t +bswap_u32(uint32_t x); + +__attribute__ ((const)) extern inline uint64_t +bswap_u64(uint64_t x); + +#define BITINDEX_NAIVE(x, bitsize) { \ + /* naive generic implementation, worst case */ \ + typeof(x) bit = 1; \ + int i = 0; \ + for (int k = 0; k < bitsize; k++) { \ + if (x & bit) { \ + indexes[i++] = offset + k + 1; \ + } \ + bit <<= 1; \ + } \ + \ + indexes[i] = 0; \ + return indexes + i; \ +} + +int * +bit_index_u32(uint32_t x, int *indexes, int offset) +{ +#if defined(HAVE_CTZ) + int prev_pos = 0; + int i = 0; + +#if defined(HAVE_POPCOUNT) + /* fast implementation using built-in popcount function */ + const int count = bit_count_u32(x); + while (i < count) { +#else + /* sligtly slower implementation without using built-in popcount */ + while(x) { +#endif + /* use ctz */ + const int a = bit_ctz_u32(x); + + prev_pos += a + 1; + x >>= a; + x >>= 1; + indexes[i++] = offset + prev_pos; + } + + indexes[i] = 0; + return indexes + i; +#else /* !defined(HAVE_CTZ) */ + BITINDEX_NAIVE(x, sizeof(uint32_t) * CHAR_BIT); +#endif +} + +int * +bit_index_u64(uint64_t x, int *indexes, int offset) { +#if defined(HAVE_CTZLL) + int prev_pos = 0; + int i = 0; + +#if defined(HAVE_POPCOUNTLL) + /* fast implementation using built-in popcount function */ + const int count = bit_count_u64(x); + while (i < count) { +#else + /* sligtly slower implementation without using built-in popcount */ + while(x) { +#endif + /* use ctz */ + const int a = bit_ctz_u64(x); + + prev_pos += a + 1; + x >>= a; + x >>= 1; + indexes[i++] = offset + prev_pos; + } + + indexes[i] = 0; + return indexes + i; +#else /* !defined(HAVE_CTZ) */ + BITINDEX_NAIVE(x, sizeof(uint64_t) * CHAR_BIT); +#endif +} + +#undef BITINDEX_NAIVE