intrinPortable.h 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. /*
  2. Copyright (c) 2018 tevador
  3. This file is part of RandomX.
  4. RandomX is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. RandomX is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with RandomX. If not, see<http://www.gnu.org/licenses/>.
  14. */
  15. #pragma once
  16. #include <cstdint>
  17. constexpr int32_t unsigned32ToSigned2sCompl(uint32_t x) {
  18. return (-1 == ~0) ? (int32_t)x : (x > INT32_MAX ? (-(int32_t)(UINT32_MAX - x) - 1) : (int32_t)x);
  19. }
  20. constexpr int64_t unsigned64ToSigned2sCompl(uint64_t x) {
  21. return (-1 == ~0) ? (int64_t)x : (x > INT64_MAX ? (-(int64_t)(UINT64_MAX - x) - 1) : (int64_t)x);
  22. }
  23. constexpr uint64_t signExtend2sCompl(uint32_t x) {
  24. return (-1 == ~0) ? (int64_t)(int32_t)(x) : (x > INT32_MAX ? (x | 0xffffffff00000000ULL) : (uint64_t)x);
  25. }
  26. #if defined(_MSC_VER)
  27. #if defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP == 2)
  28. #define __SSE2__ 1
  29. #endif
  30. #endif
  31. #ifdef __SSE2__
  32. #ifdef __GNUC__
  33. #include <x86intrin.h>
  34. #else
  35. #include <intrin.h>
  36. #endif
  37. #define PREFETCHNTA(x) _mm_prefetch((const char *)(x), _MM_HINT_NTA)
  38. #else
  39. #include <cstdint>
  40. #include <stdexcept>
  41. #include <cstdlib>
  42. #include <cmath>
  43. #include "blake2/endian.h"
  44. #define _mm_malloc(a,b) malloc(a)
  45. #define _mm_free(a) free(a)
  46. #define PREFETCHNTA(x)
  47. typedef union {
  48. uint64_t u64[2];
  49. uint32_t u32[4];
  50. uint16_t u16[8];
  51. uint8_t u8[16];
  52. } __m128i;
  53. typedef union {
  54. struct {
  55. double lo;
  56. double hi;
  57. };
  58. __m128i i;
  59. } __m128d;
  60. inline __m128d _mm_load_pd(const double* pd) {
  61. __m128d x;
  62. x.i.u64[0] = load64(pd + 0);
  63. x.i.u64[1] = load64(pd + 1);
  64. return x;
  65. }
  66. inline void _mm_store_pd(double* mem_addr, __m128d a) {
  67. store64(mem_addr + 0, a.i.u64[0]);
  68. store64(mem_addr + 1, a.i.u64[1]);
  69. }
  70. inline __m128d _mm_shuffle_pd(__m128d a, __m128d b, int imm8) {
  71. __m128d x;
  72. x.lo = (imm8 & 1) ? a.hi : a.lo;
  73. x.hi = (imm8 & 2) ? b.hi : b.lo;
  74. return x;
  75. }
  76. inline __m128d _mm_add_pd(__m128d a, __m128d b) {
  77. __m128d x;
  78. x.lo = a.lo + b.lo;
  79. x.hi = a.hi + b.hi;
  80. return x;
  81. }
  82. inline __m128d _mm_sub_pd(__m128d a, __m128d b) {
  83. __m128d x;
  84. x.lo = a.lo - b.lo;
  85. x.hi = a.hi - b.hi;
  86. return x;
  87. }
  88. inline __m128d _mm_mul_pd(__m128d a, __m128d b) {
  89. __m128d x;
  90. x.lo = a.lo * b.lo;
  91. x.hi = a.hi * b.hi;
  92. return x;
  93. }
  94. inline __m128d _mm_div_pd(__m128d a, __m128d b) {
  95. __m128d x;
  96. x.lo = a.lo / b.lo;
  97. x.hi = a.hi / b.hi;
  98. return x;
  99. }
  100. inline __m128d _mm_sqrt_pd(__m128d a) {
  101. __m128d x;
  102. x.lo = sqrt(a.lo);
  103. x.hi = sqrt(a.hi);
  104. return x;
  105. }
  106. inline __m128i _mm_set1_epi64x(uint64_t a) {
  107. __m128i x;
  108. x.u64[0] = a;
  109. x.u64[1] = a;
  110. return x;
  111. }
  112. inline __m128d _mm_castsi128_pd(__m128i a) {
  113. __m128d x;
  114. x.i = a;
  115. return x;
  116. }
  117. inline __m128d _mm_abs(__m128d xd) {
  118. xd.lo = std::fabs(xd.lo);
  119. xd.hi = std::fabs(xd.hi);
  120. return xd;
  121. }
  122. inline __m128d _mm_xor_pd(__m128d a, __m128d b) {
  123. __m128d x;
  124. x.i.u64[0] = a.i.u64[0] ^ b.i.u64[0];
  125. x.i.u64[1] = a.i.u64[1] ^ b.i.u64[1];
  126. return x;
  127. }
  128. inline __m128d _mm_and_pd(__m128d a, __m128d b) {
  129. __m128d x;
  130. x.i.u64[0] = a.i.u64[0] & b.i.u64[0];
  131. x.i.u64[1] = a.i.u64[1] & b.i.u64[1];
  132. return x;
  133. }
  134. inline __m128d _mm_or_pd(__m128d a, __m128d b) {
  135. __m128d x;
  136. x.i.u64[0] = a.i.u64[0] | b.i.u64[0];
  137. x.i.u64[1] = a.i.u64[1] | b.i.u64[1];
  138. return x;
  139. }
  140. inline __m128d _mm_set_pd(double e1, double e0) {
  141. __m128d x;
  142. x.lo = e0;
  143. x.hi = e1;
  144. return x;
  145. }
  146. inline __m128d _mm_max_pd(__m128d a, __m128d b) {
  147. __m128d x;
  148. x.lo = a.lo > b.lo ? a.lo : b.lo;
  149. x.hi = a.hi > b.hi ? a.hi : b.hi;
  150. return x;
  151. }
  152. inline __m128d _mm_cvtepi32_pd(__m128i a) {
  153. __m128d x;
  154. x.lo = (double)unsigned32ToSigned2sCompl(a.u32[0]);
  155. x.hi = (double)unsigned32ToSigned2sCompl(a.u32[1]);
  156. return x;
  157. }
  158. static const char* platformError = "Platform doesn't support hardware AES";
  159. inline __m128i _mm_aeskeygenassist_si128(__m128i key, uint8_t rcon) {
  160. throw std::runtime_error(platformError);
  161. }
  162. inline __m128i _mm_aesenc_si128(__m128i v, __m128i rkey) {
  163. throw std::runtime_error(platformError);
  164. }
  165. inline __m128i _mm_aesdec_si128(__m128i v, __m128i rkey) {
  166. throw std::runtime_error(platformError);
  167. }
  168. inline int _mm_cvtsi128_si32(__m128i v) {
  169. return v.u32[0];
  170. }
  171. inline __m128i _mm_cvtsi32_si128(int si32) {
  172. __m128i v;
  173. v.u32[0] = si32;
  174. v.u32[1] = 0;
  175. v.u32[2] = 0;
  176. v.u32[3] = 0;
  177. return v;
  178. }
  179. inline __m128i _mm_set_epi64x(int64_t _I1, int64_t _I0) {
  180. __m128i v;
  181. v.u64[0] = _I0;
  182. v.u64[1] = _I1;
  183. return v;
  184. }
  185. inline __m128i _mm_set_epi32(int _I3, int _I2, int _I1, int _I0) {
  186. __m128i v;
  187. v.u32[0] = _I0;
  188. v.u32[1] = _I1;
  189. v.u32[2] = _I2;
  190. v.u32[3] = _I3;
  191. return v;
  192. };
  193. inline __m128i _mm_xor_si128(__m128i _A, __m128i _B) {
  194. __m128i c;
  195. c.u32[0] = _A.u32[0] ^ _B.u32[0];
  196. c.u32[1] = _A.u32[1] ^ _B.u32[1];
  197. c.u32[2] = _A.u32[2] ^ _B.u32[2];
  198. c.u32[3] = _A.u32[3] ^ _B.u32[3];
  199. return c;
  200. }
  201. inline __m128i _mm_shuffle_epi32(__m128i _A, int _Imm) {
  202. __m128i c;
  203. c.u32[0] = _A.u32[_Imm & 3];
  204. c.u32[1] = _A.u32[(_Imm >> 2) & 3];
  205. c.u32[2] = _A.u32[(_Imm >> 4) & 3];
  206. c.u32[3] = _A.u32[(_Imm >> 6) & 3];
  207. return c;
  208. }
  209. inline __m128i _mm_load_si128(__m128i const*_P) {
  210. #if defined(NATIVE_LITTLE_ENDIAN)
  211. return *_P;
  212. #else
  213. uint32_t* ptr = (uint32_t*)_P;
  214. __m128i c;
  215. c.u32[0] = load32(ptr + 0);
  216. c.u32[1] = load32(ptr + 1);
  217. c.u32[2] = load32(ptr + 2);
  218. c.u32[3] = load32(ptr + 3);
  219. return c;
  220. #endif
  221. }
  222. inline void _mm_store_si128(__m128i *_P, __m128i _B) {
  223. #if defined(NATIVE_LITTLE_ENDIAN)
  224. *_P = _B;
  225. #else
  226. uint32_t* ptr = (uint32_t*)_P;
  227. store32(ptr + 0, _B.u32[0]);
  228. store32(ptr + 1, _B.u32[1]);
  229. store32(ptr + 2, _B.u32[2]);
  230. store32(ptr + 3, _B.u32[3]);
  231. #endif
  232. }
  233. inline __m128i _mm_slli_si128(__m128i _A, int _Imm) {
  234. _Imm &= 255;
  235. if (_Imm > 15) {
  236. _A.u64[0] = 0;
  237. _A.u64[1] = 0;
  238. }
  239. else {
  240. for (int i = 15; i >= _Imm; --i) {
  241. _A.u8[i] = _A.u8[i - _Imm];
  242. }
  243. for (int i = 0; i < _Imm; ++i) {
  244. _A.u8[i] = 0;
  245. }
  246. }
  247. return _A;
  248. }
  249. inline __m128i _mm_loadl_epi64(__m128i const* mem_addr) {
  250. __m128i x;
  251. x.u64[0] = load64(mem_addr);
  252. return x;
  253. }
  254. #endif
  255. constexpr int RoundToNearest = 0;
  256. constexpr int RoundDown = 1;
  257. constexpr int RoundUp = 2;
  258. constexpr int RoundToZero = 3;
  259. inline __m128d load_cvt_i32x2(const void* addr) {
  260. __m128i ix = _mm_loadl_epi64((const __m128i*)addr);
  261. return _mm_cvtepi32_pd(ix);
  262. }
  263. template<int E>
  264. constexpr uint64_t ieee_get_exponent_mask() {
  265. static_assert(E > -1023, "Invalid exponent value");
  266. return (uint64_t)(E + 1023U) << 52;
  267. }
  268. template<int E>
  269. __m128d ieee_set_exponent(__m128d x) {
  270. static_assert(E > -1023, "Invalid exponent value");
  271. constexpr uint64_t mantissaMask64 = (1ULL << 52) - 1;
  272. const __m128d mantissaMask = _mm_castsi128_pd(_mm_set_epi64x(mantissaMask64, mantissaMask64));
  273. constexpr uint64_t exponent64 = (uint64_t)(E + 1023U) << 52;
  274. const __m128d exponentMask = _mm_castsi128_pd(_mm_set_epi64x(exponent64, exponent64));
  275. x = _mm_and_pd(x, mantissaMask);
  276. x = _mm_or_pd(x, exponentMask);
  277. return x;
  278. }
  279. double loadDoublePortable(const void* addr);
  280. uint64_t mulh(uint64_t, uint64_t);
  281. int64_t smulh(int64_t, int64_t);
  282. uint64_t rotl(uint64_t, int);
  283. uint64_t rotr(uint64_t, int);
  284. void initFpu();
  285. void setRoundMode(uint32_t);
  286. bool condition(uint32_t, uint32_t, uint32_t);