mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
185 lines
5.1 KiB
C++
185 lines
5.1 KiB
C++
#ifndef SIMDJSON_PORTABILITY_H
|
|
#define SIMDJSON_PORTABILITY_H
|
|
|
|
#if defined(__x86_64__) || defined(_M_AMD64)
|
|
#define IS_X86_64 1
|
|
#endif
|
|
#if defined(__aarch64__) || defined(_M_ARM64)
|
|
#define IS_ARM64 1
|
|
#endif
|
|
|
|
// this is almost standard?
|
|
#define STRINGIFY(a) #a
|
|
|
|
// we are going to use runtime dispatch
|
|
#ifdef IS_X86_64
|
|
#ifdef __clang__
|
|
// clang does not have GCC push pop
|
|
// warning: clang attribute push can't be used within a namespace in clang up
|
|
// til 8.0 so TARGET_REGION and UNTARGET_REGION must be *outside* of a
|
|
// namespace.
|
|
#define TARGET_REGION(T) \
|
|
_Pragma(STRINGIFY( \
|
|
clang attribute push(__attribute__((target(T))), apply_to = function)))
|
|
#define UNTARGET_REGION _Pragma("clang attribute pop")
|
|
#elif defined(__GNUC__)
|
|
// GCC is easier
|
|
#define TARGET_REGION(T) \
|
|
_Pragma("GCC push_options") _Pragma(STRINGIFY(GCC target(T)))
|
|
#define UNTARGET_REGION _Pragma("GCC pop_options")
|
|
#endif // clang then gcc
|
|
|
|
#endif // x86
|
|
|
|
// Default target region macros don't do anything.
|
|
#ifndef TARGET_REGION
|
|
#define TARGET_REGION(T)
|
|
#define UNTARGET_REGION
|
|
#endif
|
|
|
|
// under GCC and CLANG, we use these two macros
|
|
#define TARGET_HASWELL TARGET_REGION("avx2,bmi,pclmul")
|
|
#define TARGET_WESTMERE TARGET_REGION("sse4.2,pclmul")
|
|
#define TARGET_ARM64
|
|
|
|
#ifdef _MSC_VER
|
|
#include <intrin.h>
|
|
#else
|
|
#if IS_X86_64
|
|
#include <x86intrin.h>
|
|
#elif IS_ARM64
|
|
#include <arm_neon.h>
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(__clang__)
|
|
#define NO_SANITIZE_UNDEFINED __attribute__((no_sanitize("undefined")))
|
|
#elif defined(__GNUC__)
|
|
#define NO_SANITIZE_UNDEFINED __attribute__((no_sanitize_undefined))
|
|
#else
|
|
#define NO_SANITIZE_UNDEFINED
|
|
#endif
|
|
|
|
#ifdef _MSC_VER
|
|
/* Microsoft C/C++-compatible compiler */
|
|
#include <cstdint>
|
|
#include <iso646.h>
|
|
|
|
namespace simdjson {
|
|
static inline bool add_overflow(uint64_t value1, uint64_t value2,
|
|
uint64_t *result) {
|
|
return _addcarry_u64(0, value1, value2,
|
|
reinterpret_cast<unsigned __int64 *>(result));
|
|
}
|
|
|
|
#pragma intrinsic(_umul128)
|
|
static inline bool mul_overflow(uint64_t value1, uint64_t value2,
|
|
uint64_t *result) {
|
|
uint64_t high;
|
|
*result = _umul128(value1, value2, &high);
|
|
return high;
|
|
}
|
|
|
|
static inline int trailing_zeroes(uint64_t input_num) {
|
|
return static_cast<int>(_tzcnt_u64(input_num));
|
|
}
|
|
|
|
static inline int leading_zeroes(uint64_t input_num) {
|
|
return static_cast<int>(_lzcnt_u64(input_num));
|
|
}
|
|
|
|
static inline int hamming(uint64_t input_num) {
|
|
#ifdef _WIN64 // highly recommended!!!
|
|
return (int)__popcnt64(input_num);
|
|
#else // if we must support 32-bit Windows
|
|
return (int)(__popcnt((uint32_t)input_num) +
|
|
__popcnt((uint32_t)(input_num >> 32)));
|
|
#endif
|
|
}
|
|
} // namespace simdjson
|
|
#else
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
|
|
namespace simdjson {
|
|
static inline bool add_overflow(uint64_t value1, uint64_t value2,
|
|
uint64_t *result) {
|
|
return __builtin_uaddll_overflow(value1, value2,
|
|
(unsigned long long *)result);
|
|
}
|
|
static inline bool mul_overflow(uint64_t value1, uint64_t value2,
|
|
uint64_t *result) {
|
|
return __builtin_umulll_overflow(value1, value2,
|
|
(unsigned long long *)result);
|
|
}
|
|
|
|
/* result might be undefined when input_num is zero */
|
|
static inline NO_SANITIZE_UNDEFINED int trailing_zeroes(uint64_t input_num) {
|
|
#ifdef __BMI__ // tzcnt is BMI1
|
|
return _tzcnt_u64(input_num);
|
|
#else
|
|
return __builtin_ctzll(input_num);
|
|
#endif
|
|
}
|
|
|
|
/* result might be undefined when input_num is zero */
|
|
static inline int leading_zeroes(uint64_t input_num) {
|
|
#ifdef __BMI2__
|
|
return _lzcnt_u64(input_num);
|
|
#else
|
|
return __builtin_clzll(input_num);
|
|
#endif
|
|
}
|
|
|
|
/* result might be undefined when input_num is zero */
|
|
static inline int hamming(uint64_t input_num) {
|
|
#ifdef __POPCOUNT__
|
|
return _popcnt64(input_num);
|
|
#else
|
|
return __builtin_popcountll(input_num);
|
|
#endif
|
|
}
|
|
} // namespace simdjson
|
|
#endif // _MSC_VER
|
|
|
|
namespace simdjson {
|
|
// portable version of posix_memalign
|
|
static inline void *aligned_malloc(size_t alignment, size_t size) {
|
|
void *p;
|
|
#ifdef _MSC_VER
|
|
p = _aligned_malloc(size, alignment);
|
|
#elif defined(__MINGW32__) || defined(__MINGW64__)
|
|
p = __mingw_aligned_malloc(size, alignment);
|
|
#else
|
|
// somehow, if this is used before including "x86intrin.h", it creates an
|
|
// implicit defined warning.
|
|
if (posix_memalign(&p, alignment, size) != 0) {
|
|
return nullptr;
|
|
}
|
|
#endif
|
|
return p;
|
|
}
|
|
|
|
static inline char *aligned_malloc_char(size_t alignment, size_t size) {
|
|
return (char *)aligned_malloc(alignment, size);
|
|
}
|
|
|
|
static inline void aligned_free(void *mem_block) {
|
|
if (mem_block == nullptr) {
|
|
return;
|
|
}
|
|
#ifdef _MSC_VER
|
|
_aligned_free(mem_block);
|
|
#elif defined(__MINGW32__) || defined(__MINGW64__)
|
|
__mingw_aligned_free(mem_block);
|
|
#else
|
|
free(mem_block);
|
|
#endif
|
|
}
|
|
|
|
static inline void aligned_free_char(char *mem_block) {
|
|
aligned_free((void *)mem_block);
|
|
}
|
|
} // namespace simdjson
|
|
#endif // SIMDJSON_PORTABILITY_H
|