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 */ |
118 | static 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 */ |
134 | static 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 */ |
153 | static 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 */ |
163 | static 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 |
184 | extern 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 |
188 | static 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 | |
202 | static 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 | |
228 | static 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 | |