codec_choose.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. #include <stdbool.h>
  2. #include <stdint.h>
  3. #include <stddef.h>
  4. #include <stdint.h>
  5. #include <stdio.h>
  6. #include "../include/libbase64.h"
  7. #include "codecs.h"
  8. #include "config.h"
  9. #include "env.h"
  10. #if (__x86_64__ || __i386__ || _M_X86 || _M_X64)
  11. #define BASE64_X86
  12. #if (HAVE_SSSE3 || HAVE_SSE41 || HAVE_SSE42 || HAVE_AVX || HAVE_AVX2 || HAVE_AVX512)
  13. #define BASE64_X86_SIMD
  14. #endif
  15. #endif
  16. #ifdef BASE64_X86
  17. #ifdef _MSC_VER
  18. #include <intrin.h>
  19. #define __cpuid_count(__level, __count, __eax, __ebx, __ecx, __edx) \
  20. { \
  21. int info[4]; \
  22. __cpuidex(info, __level, __count); \
  23. __eax = info[0]; \
  24. __ebx = info[1]; \
  25. __ecx = info[2]; \
  26. __edx = info[3]; \
  27. }
  28. #define __cpuid(__level, __eax, __ebx, __ecx, __edx) \
  29. __cpuid_count(__level, 0, __eax, __ebx, __ecx, __edx)
  30. #else
  31. #include <cpuid.h>
  32. #if HAVE_AVX512 || HAVE_AVX2 || HAVE_AVX
  33. #if ((__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 2) || (__clang_major__ >= 3))
  34. static inline uint64_t _xgetbv (uint32_t index)
  35. {
  36. uint32_t eax, edx;
  37. __asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index));
  38. return ((uint64_t)edx << 32) | eax;
  39. }
  40. #else
  41. #error "Platform not supported"
  42. #endif
  43. #endif
  44. #endif
  45. #ifndef bit_AVX512vl
  46. #define bit_AVX512vl (1 << 31)
  47. #endif
  48. #ifndef bit_AVX512vbmi
  49. #define bit_AVX512vbmi (1 << 1)
  50. #endif
  51. #ifndef bit_AVX2
  52. #define bit_AVX2 (1 << 5)
  53. #endif
  54. #ifndef bit_SSSE3
  55. #define bit_SSSE3 (1 << 9)
  56. #endif
  57. #ifndef bit_SSE41
  58. #define bit_SSE41 (1 << 19)
  59. #endif
  60. #ifndef bit_SSE42
  61. #define bit_SSE42 (1 << 20)
  62. #endif
  63. #ifndef bit_AVX
  64. #define bit_AVX (1 << 28)
  65. #endif
  66. #define bit_XSAVE_XRSTORE (1 << 27)
  67. #ifndef _XCR_XFEATURE_ENABLED_MASK
  68. #define _XCR_XFEATURE_ENABLED_MASK 0
  69. #endif
  70. #define _XCR_XMM_AND_YMM_STATE_ENABLED_BY_OS 0x6
  71. #endif
  72. // Function declarations:
  73. #define BASE64_CODEC_FUNCS(arch) \
  74. BASE64_ENC_FUNCTION(arch); \
  75. BASE64_DEC_FUNCTION(arch); \
  76. BASE64_CODEC_FUNCS(avx512)
  77. BASE64_CODEC_FUNCS(avx2)
  78. BASE64_CODEC_FUNCS(neon32)
  79. BASE64_CODEC_FUNCS(neon64)
  80. BASE64_CODEC_FUNCS(plain)
  81. BASE64_CODEC_FUNCS(ssse3)
  82. BASE64_CODEC_FUNCS(sse41)
  83. BASE64_CODEC_FUNCS(sse42)
  84. BASE64_CODEC_FUNCS(avx)
  85. static bool
  86. codec_choose_forced (struct codec *codec, int flags)
  87. {
  88. // If the user wants to use a certain codec,
  89. // always allow it, even if the codec is a no-op.
  90. // For testing purposes.
  91. if (!(flags & 0xFFFF)) {
  92. return false;
  93. }
  94. if (flags & BASE64_FORCE_AVX2) {
  95. codec->enc = base64_stream_encode_avx2;
  96. codec->dec = base64_stream_decode_avx2;
  97. return true;
  98. }
  99. if (flags & BASE64_FORCE_NEON32) {
  100. codec->enc = base64_stream_encode_neon32;
  101. codec->dec = base64_stream_decode_neon32;
  102. return true;
  103. }
  104. if (flags & BASE64_FORCE_NEON64) {
  105. codec->enc = base64_stream_encode_neon64;
  106. codec->dec = base64_stream_decode_neon64;
  107. return true;
  108. }
  109. if (flags & BASE64_FORCE_PLAIN) {
  110. codec->enc = base64_stream_encode_plain;
  111. codec->dec = base64_stream_decode_plain;
  112. return true;
  113. }
  114. if (flags & BASE64_FORCE_SSSE3) {
  115. codec->enc = base64_stream_encode_ssse3;
  116. codec->dec = base64_stream_decode_ssse3;
  117. return true;
  118. }
  119. if (flags & BASE64_FORCE_SSE41) {
  120. codec->enc = base64_stream_encode_sse41;
  121. codec->dec = base64_stream_decode_sse41;
  122. return true;
  123. }
  124. if (flags & BASE64_FORCE_SSE42) {
  125. codec->enc = base64_stream_encode_sse42;
  126. codec->dec = base64_stream_decode_sse42;
  127. return true;
  128. }
  129. if (flags & BASE64_FORCE_AVX) {
  130. codec->enc = base64_stream_encode_avx;
  131. codec->dec = base64_stream_decode_avx;
  132. return true;
  133. }
  134. if (flags & BASE64_FORCE_AVX512) {
  135. codec->enc = base64_stream_encode_avx512;
  136. codec->dec = base64_stream_decode_avx512;
  137. return true;
  138. }
  139. return false;
  140. }
  141. static bool
  142. codec_choose_arm (struct codec *codec)
  143. {
  144. #if (defined(__ARM_NEON__) || defined(__ARM_NEON)) && ((defined(__aarch64__) && HAVE_NEON64) || HAVE_NEON32)
  145. // Unfortunately there is no portable way to check for NEON
  146. // support at runtime from userland in the same way that x86
  147. // has cpuid, so just stick to the compile-time configuration:
  148. #if defined(__aarch64__) && HAVE_NEON64
  149. codec->enc = base64_stream_encode_neon64;
  150. codec->dec = base64_stream_decode_neon64;
  151. #else
  152. codec->enc = base64_stream_encode_neon32;
  153. codec->dec = base64_stream_decode_neon32;
  154. #endif
  155. return true;
  156. #else
  157. (void)codec;
  158. return false;
  159. #endif
  160. }
  161. static bool
  162. codec_choose_x86 (struct codec *codec)
  163. {
  164. #ifdef BASE64_X86_SIMD
  165. unsigned int eax, ebx = 0, ecx = 0, edx;
  166. unsigned int max_level;
  167. #ifdef _MSC_VER
  168. int info[4];
  169. __cpuidex(info, 0, 0);
  170. max_level = info[0];
  171. #else
  172. max_level = __get_cpuid_max(0, NULL);
  173. #endif
  174. #if HAVE_AVX512 || HAVE_AVX2 || HAVE_AVX
  175. // Check for AVX/AVX2/AVX512 support:
  176. // Checking for AVX requires 3 things:
  177. // 1) CPUID indicates that the OS uses XSAVE and XRSTORE instructions
  178. // (allowing saving YMM registers on context switch)
  179. // 2) CPUID indicates support for AVX
  180. // 3) XGETBV indicates the AVX registers will be saved and restored on
  181. // context switch
  182. //
  183. // Note that XGETBV is only available on 686 or later CPUs, so the
  184. // instruction needs to be conditionally run.
  185. if (max_level >= 1) {
  186. __cpuid_count(1, 0, eax, ebx, ecx, edx);
  187. if (ecx & bit_XSAVE_XRSTORE) {
  188. uint64_t xcr_mask;
  189. xcr_mask = _xgetbv(_XCR_XFEATURE_ENABLED_MASK);
  190. if ((xcr_mask & _XCR_XMM_AND_YMM_STATE_ENABLED_BY_OS) == _XCR_XMM_AND_YMM_STATE_ENABLED_BY_OS) { // check multiple bits at once
  191. #if HAVE_AVX512
  192. if (max_level >= 7) {
  193. __cpuid_count(7, 0, eax, ebx, ecx, edx);
  194. if ((ebx & bit_AVX512vl) && (ecx & bit_AVX512vbmi)) {
  195. codec->enc = base64_stream_encode_avx512;
  196. codec->dec = base64_stream_decode_avx512;
  197. return true;
  198. }
  199. }
  200. #endif
  201. #if HAVE_AVX2
  202. if (max_level >= 7) {
  203. __cpuid_count(7, 0, eax, ebx, ecx, edx);
  204. if (ebx & bit_AVX2) {
  205. codec->enc = base64_stream_encode_avx2;
  206. codec->dec = base64_stream_decode_avx2;
  207. return true;
  208. }
  209. }
  210. #endif
  211. #if HAVE_AVX
  212. __cpuid_count(1, 0, eax, ebx, ecx, edx);
  213. if (ecx & bit_AVX) {
  214. codec->enc = base64_stream_encode_avx;
  215. codec->dec = base64_stream_decode_avx;
  216. return true;
  217. }
  218. #endif
  219. }
  220. }
  221. }
  222. #endif
  223. #if HAVE_SSE42
  224. // Check for SSE42 support:
  225. if (max_level >= 1) {
  226. __cpuid(1, eax, ebx, ecx, edx);
  227. if (ecx & bit_SSE42) {
  228. codec->enc = base64_stream_encode_sse42;
  229. codec->dec = base64_stream_decode_sse42;
  230. return true;
  231. }
  232. }
  233. #endif
  234. #if HAVE_SSE41
  235. // Check for SSE41 support:
  236. if (max_level >= 1) {
  237. __cpuid(1, eax, ebx, ecx, edx);
  238. if (ecx & bit_SSE41) {
  239. codec->enc = base64_stream_encode_sse41;
  240. codec->dec = base64_stream_decode_sse41;
  241. return true;
  242. }
  243. }
  244. #endif
  245. #if HAVE_SSSE3
  246. // Check for SSSE3 support:
  247. if (max_level >= 1) {
  248. __cpuid(1, eax, ebx, ecx, edx);
  249. if (ecx & bit_SSSE3) {
  250. codec->enc = base64_stream_encode_ssse3;
  251. codec->dec = base64_stream_decode_ssse3;
  252. return true;
  253. }
  254. }
  255. #endif
  256. #else
  257. (void)codec;
  258. #endif
  259. return false;
  260. }
  261. void
  262. codec_choose (struct codec *codec, int flags)
  263. {
  264. // User forced a codec:
  265. if (codec_choose_forced(codec, flags)) {
  266. return;
  267. }
  268. // Runtime feature detection:
  269. if (codec_choose_arm(codec)) {
  270. return;
  271. }
  272. if (codec_choose_x86(codec)) {
  273. return;
  274. }
  275. codec->enc = base64_stream_encode_plain;
  276. codec->dec = base64_stream_decode_plain;
  277. }