1/*
2 * portability.h
3 *
4 */
5
6#ifndef INCLUDE_PORTABILITY_H_
7#define INCLUDE_PORTABILITY_H_
8
9#ifndef _GNU_SOURCE
10#define _GNU_SOURCE
11#endif
12#ifndef __STDC_FORMAT_MACROS
13#define __STDC_FORMAT_MACROS 1
14#endif
15
16#if !(defined(_POSIX_C_SOURCE)) || (_POSIX_C_SOURCE < 200809L)
17#define _POSIX_C_SOURCE 200809L
18#endif
19#if !(defined(_XOPEN_SOURCE)) || (_XOPEN_SOURCE < 700)
20#define _XOPEN_SOURCE 700
21#endif
22
23#include <stdbool.h>
24#include <stdint.h>
25#include <stdlib.h> // will provide posix_memalign with _POSIX_C_SOURCE as defined above
26#if !(defined(__APPLE__)) && !(defined(__FreeBSD__))
27#include <malloc.h> // this should never be needed but there are some reports that it is needed.
28#endif
29
30
31#if defined(_MSC_VER) && !defined(__clang__) && !defined(_WIN64) && !defined(ROARING_ACK_32BIT)
32#pragma message( \
33 "You appear to be attempting a 32-bit build under Visual Studio. We recommend a 64-bit build instead.")
34#endif
35
36#if defined(__SIZEOF_LONG_LONG__) && __SIZEOF_LONG_LONG__ != 8
37#error This code assumes 64-bit long longs (by use of the GCC intrinsics). Your system is not currently supported.
38#endif
39
40#if defined(_MSC_VER)
41#define __restrict__ __restrict
42#endif
43
44#ifndef DISABLE_X64 // some users may want to compile as if they did not have
45 // an x64 processor
46
47///////////////////////
48/// We support X64 hardware in the following manner:
49///
50/// if IS_X64 is defined then we have at least SSE and SSE2
51/// (All Intel processors sold in the recent past have at least SSE and SSE2 support,
52/// going back to the Pentium 4.)
53///
54/// if USESSE4 is defined then we assume at least SSE4.2, SSE4.1,
55/// SSSE3, SSE3... + IS_X64
56/// if USEAVX is defined, then we assume AVX2, AVX + USESSE4
57///
58/// So if you have hardware that supports AVX but not AVX2, then "USEAVX"
59/// won't be enabled.
60/// If you have hardware that supports SSE4.1, but not SSE4.2, then USESSE4
61/// won't be defined.
62//////////////////////
63
64// unless DISABLEAVX was defined, if we have __AVX2__, we enable AVX
65#if (!defined(USEAVX)) && (!defined(DISABLEAVX)) && (defined(__AVX2__))
66#define USEAVX
67#endif
68
69// if we have __SSE4_2__, we enable SSE4
70#if (defined(__POPCNT__)) && (defined(__SSE4_2__))
71#define USESSE4
72#endif
73
74#if defined(USEAVX) || defined(__x86_64__) || defined(_M_X64)
75// we have an x64 processor
76#define IS_X64
77// we include the intrinsic header
78#ifndef _MSC_VER
79/* Non-Microsoft C/C++-compatible compiler */
80#include <x86intrin.h> // on some recent GCC, this will declare posix_memalign
81#endif
82#endif
83
84#if !defined(USENEON) && !defined(DISABLENEON) && defined(__ARM_NEON)
85# define USENEON
86#endif
87#if defined(USENEON)
88# include <arm_neon.h>
89#endif
90
91#ifndef _MSC_VER
92/* Non-Microsoft C/C++-compatible compiler, assumes that it supports inline
93 * assembly */
94#define ROARING_INLINE_ASM
95#endif
96
97#ifdef USEAVX
98#define USESSE4 // if we have AVX, then we have SSE4
99#define USE_BMI // we assume that AVX2 and BMI go hand and hand
100#define USEAVX2FORDECODING // optimization
101// vector operations should work on not just AVX
102#define ROARING_VECTOR_OPERATIONS_ENABLED // vector unions (optimization)
103#endif
104
105#endif // DISABLE_X64
106
107#ifdef _MSC_VER
108/* Microsoft C/C++-compatible compiler */
109#include <intrin.h>
110
111#ifndef __clang__ // if one compiles with MSVC *with* clang, then these
112 // intrinsics are defined!!!
113// sadly there is no way to check whether we are missing these intrinsics
114// specifically.
115
116/* wrappers for Visual Studio built-ins that look like gcc built-ins */
117/* result might be undefined when input_num is zero */
118static inline int __builtin_ctzll(unsigned long long input_num) {
119 unsigned long index;
120#ifdef _WIN64 // highly recommended!!!
121 _BitScanForward64(&index, input_num);
122#else // if we must support 32-bit Windows
123 if ((uint32_t)input_num != 0) {
124 _BitScanForward(&index, (uint32_t)input_num);
125 } else {
126 _BitScanForward(&index, (uint32_t)(input_num >> 32));
127 index += 32;
128 }
129#endif
130 return index;
131}
132
133/* result might be undefined when input_num is zero */
134static inline int __builtin_clzll(unsigned long long input_num) {
135 unsigned long index;
136#ifdef _WIN64 // highly recommended!!!
137 _BitScanReverse64(&index, input_num);
138#else // if we must support 32-bit Windows
139 if (input_num > 0xFFFFFFFF) {
140 _BitScanReverse(&index, (uint32_t)(input_num >> 32));
141 index += 32;
142 } else {
143 _BitScanReverse(&index, (uint32_t)(input_num));
144 }
145#endif
146 return 63 - index;
147}
148
149/* result might be undefined when input_num is zero */
150#ifdef USESSE4
151/* POPCNT support was added to processors around the release of SSE4.2 */
152/* USESSE4 flag guarantees POPCNT support */
153static inline int __builtin_popcountll(unsigned long long input_num) {
154#ifdef _WIN64 // highly recommended!!!
155 return (int)__popcnt64(input_num);
156#else // if we must support 32-bit Windows
157 return (int)(__popcnt((uint32_t)input_num) +
158 __popcnt((uint32_t)(input_num >> 32)));
159#endif
160}
161#else
162/* software implementation avoids POPCNT */
163static inline int __builtin_popcountll(unsigned long long input_num) {
164 const uint64_t m1 = 0x5555555555555555; //binary: 0101...
165 const uint64_t m2 = 0x3333333333333333; //binary: 00110011..
166 const uint64_t m4 = 0x0f0f0f0f0f0f0f0f; //binary: 4 zeros, 4 ones ...
167 const uint64_t h01 = 0x0101010101010101; //the sum of 256 to the power of 0,1,2,3...
168
169 input_num -= (input_num >> 1) & m1;
170 input_num = (input_num & m2) + ((input_num >> 2) & m2);
171 input_num = (input_num + (input_num >> 4)) & m4;
172 return (input_num * h01) >> 56;
173}
174#endif
175
176/* Use #define so this is effective even under /Ob0 (no inline) */
177#define __builtin_unreachable() __assume(0)
178#endif
179
180#endif
181
182// without the following, we get lots of warnings about posix_memalign
183#ifndef __cplusplus
184extern int posix_memalign(void **__memptr, size_t __alignment, size_t __size);
185#endif //__cplusplus // C++ does not have a well defined signature
186
187// portable version of posix_memalign
188static inline void *roaring_bitmap_aligned_malloc(size_t alignment, size_t size) {
189 void *p;
190#ifdef _MSC_VER
191 p = _aligned_malloc(size, alignment);
192#elif defined(__MINGW32__) || defined(__MINGW64__)
193 p = __mingw_aligned_malloc(size, alignment);
194#else
195 // somehow, if this is used before including "x86intrin.h", it creates an
196 // implicit defined warning.
197 if (posix_memalign(&p, alignment, size) != 0) return NULL;
198#endif
199 return p;
200}
201
202static inline void roaring_bitmap_aligned_free(void *memblock) {
203#ifdef _MSC_VER
204 _aligned_free(memblock);
205#elif defined(__MINGW32__) || defined(__MINGW64__)
206 __mingw_aligned_free(memblock);
207#else
208 free(memblock);
209#endif
210}
211
212#if defined(_MSC_VER)
213#define ALIGNED(x) __declspec(align(x))
214#else
215#if defined(__GNUC__)
216#define ALIGNED(x) __attribute__((aligned(x)))
217#endif
218#endif
219
220#ifdef __GNUC__
221#define WARN_UNUSED __attribute__((warn_unused_result))
222#else
223#define WARN_UNUSED
224#endif
225
226#define IS_BIG_ENDIAN (*(uint16_t *)"\0\xff" < 0x100)
227
228static inline int hamming(uint64_t x) {
229#ifdef USESSE4
230 return (int) _mm_popcnt_u64(x);
231#else
232 // won't work under visual studio, but hopeful we have _mm_popcnt_u64 in
233 // many cases
234 return __builtin_popcountll(x);
235#endif
236}
237
238#ifndef UINT64_C
239#define UINT64_C(c) (c##ULL)
240#endif
241
242#ifndef UINT32_C
243#define UINT32_C(c) (c##UL)
244#endif
245
246#endif /* INCLUDE_PORTABILITY_H_ */
247