instructionsPortable.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  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. //#define DEBUG
  16. #include "instructions.hpp"
  17. #include "intrinPortable.h"
  18. #pragma STDC FENV_ACCESS on
  19. #include <cfenv>
  20. #include <cmath>
  21. #ifdef DEBUG
  22. #include <iostream>
  23. #endif
  24. #if defined(__SIZEOF_INT128__)
  25. typedef unsigned __int128 uint128_t;
  26. typedef __int128 int128_t;
  27. static inline uint64_t __umulhi64(uint64_t a, uint64_t b) {
  28. return ((uint128_t)a * b) >> 64;
  29. }
  30. static inline uint64_t __imulhi64(int64_t a, int64_t b) {
  31. return ((int128_t)a * b) >> 64;
  32. }
  33. #define umulhi64 __umulhi64
  34. #define imulhi64 __imulhi64
  35. #endif
  36. #if defined(_MSC_VER)
  37. #define HAS_VALUE(X) X ## 0
  38. #define EVAL_DEFINE(X) HAS_VALUE(X)
  39. #include <intrin.h>
  40. #include <stdlib.h>
  41. #define ror64 _rotr64
  42. #define rol64 _rotl64
  43. #if EVAL_DEFINE(__MACHINEARM64_X64(1))
  44. #define umulhi64 __umulh
  45. #endif
  46. #if EVAL_DEFINE(__MACHINEX64(1))
  47. static inline uint64_t __imulhi64(int64_t a, int64_t b) {
  48. int64_t hi;
  49. _mul128(a, b, &hi);
  50. return hi;
  51. }
  52. #define imulhi64 __imulhi64
  53. #endif
  54. static inline uint32_t _setRoundMode(uint32_t mode) {
  55. return _controlfp(mode, _MCW_RC);
  56. }
  57. #define setRoundMode _setRoundMode
  58. #endif
  59. #ifndef setRoundMode
  60. #define setRoundMode fesetround
  61. #endif
  62. #ifndef ror64
  63. static inline uint64_t __ror64(uint64_t a, int b) {
  64. return (a >> b) | (a << (64 - b));
  65. }
  66. #define ror64 __ror64
  67. #endif
  68. #ifndef rol64
  69. static inline uint64_t __rol64(uint64_t a, int b) {
  70. return (a << b) | (a >> (64 - b));
  71. }
  72. #define rol64 __rol64
  73. #endif
  74. #ifndef sar64
  75. #include <type_traits>
  76. constexpr int64_t builtintShr64(int64_t value, int shift) noexcept {
  77. return value >> shift;
  78. }
  79. struct UsesArithmeticShift : std::integral_constant<bool, builtintShr64(-1LL, 1) == -1LL> {
  80. };
  81. static inline int64_t __sar64(int64_t a, int b) {
  82. return UsesArithmeticShift::value ? builtintShr64(a, b) : (a < 0 ? ~(~a >> b) : a >> b);
  83. }
  84. #define sar64 __sar64
  85. #endif
  86. #ifndef umulhi64
  87. #define LO(x) ((x)&0xffffffff)
  88. #define HI(x) ((x)>>32)
  89. static inline uint64_t __umulhi64(uint64_t a, uint64_t b) {
  90. uint64_t ah = HI(a), al = LO(a);
  91. uint64_t bh = HI(b), bl = LO(b);
  92. uint64_t x00 = al * bl;
  93. uint64_t x01 = al * bh;
  94. uint64_t x10 = ah * bl;
  95. uint64_t x11 = ah * bh;
  96. uint64_t m1 = LO(x10) + LO(x01) + HI(x00);
  97. uint64_t m2 = HI(x10) + HI(x01) + LO(x11) + HI(m1);
  98. uint64_t m3 = HI(x11) + HI(m2);
  99. return (m3 << 32) + LO(m2);
  100. }
  101. #define umulhi64 __umulhi64
  102. #endif
  103. #ifndef imulhi64
  104. static inline int64_t __imulhi64(int64_t a, int64_t b) {
  105. int64_t hi = umulhi64(a, b);
  106. if (a < 0LL) hi -= b;
  107. if (b < 0LL) hi -= a;
  108. return hi;
  109. }
  110. #define imulhi64 __imulhi64
  111. #endif
  112. // avoid undefined behavior of signed overflow
  113. static inline int32_t safeSub(int32_t a, int32_t b) {
  114. return int32_t(uint32_t(a) - uint32_t(b));
  115. }
  116. #if __GNUC__ >= 5
  117. #undef __has_builtin
  118. #define __has_builtin(x) 1
  119. #endif
  120. #if defined(__has_builtin)
  121. #if __has_builtin(__builtin_sub_overflow)
  122. static inline bool __subOverflow(int32_t a, int32_t b) {
  123. int32_t temp;
  124. return __builtin_sub_overflow(a, b, &temp);
  125. }
  126. #define subOverflow __subOverflow
  127. #endif
  128. #endif
  129. #ifndef subOverflow
  130. static inline bool __subOverflow(int32_t a, int32_t b) {
  131. auto c = safeSub(a, b);
  132. return (c < a) != (b > 0);
  133. }
  134. #define subOverflow __subOverflow
  135. #endif
  136. static inline double FlushDenormalNaN(double x) {
  137. int fpc = std::fpclassify(x);
  138. if (fpc == FP_SUBNORMAL || fpc == FP_NAN) {
  139. return 0.0;
  140. }
  141. return x;
  142. }
  143. static inline double FlushNaN(double x) {
  144. return x != x ? 0.0 : x;
  145. }
  146. namespace RandomX {
  147. extern "C" {
  148. void ADD_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  149. c.u64 = a.u64 + b.u64;
  150. }
  151. void ADD_32(convertible_t& a, convertible_t& b, convertible_t& c) {
  152. c.u64 = a.u32 + b.u32;
  153. }
  154. void SUB_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  155. c.u64 = a.u64 - b.u64;
  156. }
  157. void SUB_32(convertible_t& a, convertible_t& b, convertible_t& c) {
  158. c.u64 = a.u32 - b.u32;
  159. }
  160. void MUL_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  161. c.u64 = a.u64 * b.u64;
  162. }
  163. void MULH_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  164. c.u64 = umulhi64(a.u64, b.u64);
  165. }
  166. void MUL_32(convertible_t& a, convertible_t& b, convertible_t& c) {
  167. c.u64 = (uint64_t)a.u32 * b.u32;
  168. }
  169. void IMUL_32(convertible_t& a, convertible_t& b, convertible_t& c) {
  170. c.i64 = (int64_t)a.i32 * b.i32;
  171. }
  172. void IMULH_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  173. c.i64 = imulhi64(a.i64, b.i64);
  174. }
  175. void DIV_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  176. c.u64 = a.u64 / (b.u32 != 0 ? b.u32 : 1U);
  177. }
  178. void IDIV_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  179. if (a.i64 == INT64_MIN && b.i32 == -1)
  180. c.i64 = INT64_MIN;
  181. else
  182. c.i64 = a.i64 / (b.i32 != 0 ? b.i32 : 1);
  183. }
  184. void AND_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  185. c.u64 = a.u64 & b.u64;
  186. }
  187. void AND_32(convertible_t& a, convertible_t& b, convertible_t& c) {
  188. c.u64 = a.u32 & b.u32;
  189. }
  190. void OR_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  191. c.u64 = a.u64 | b.u64;
  192. }
  193. void OR_32(convertible_t& a, convertible_t& b, convertible_t& c) {
  194. c.u64 = a.u32 | b.u32;
  195. }
  196. void XOR_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  197. c.u64 = a.u64 ^ b.u64;
  198. }
  199. void XOR_32(convertible_t& a, convertible_t& b, convertible_t& c) {
  200. c.u64 = a.u32 ^ b.u32;
  201. }
  202. void SHL_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  203. c.u64 = a.u64 << (b.u64 & 63);
  204. }
  205. void SHR_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  206. c.u64 = a.u64 >> (b.u64 & 63);
  207. }
  208. void SAR_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  209. c.u64 = sar64(a.i64, b.u64 & 63);
  210. }
  211. void ROL_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  212. c.u64 = rol64(a.u64, (b.u64 & 63));
  213. }
  214. void ROR_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  215. c.u64 = ror64(a.u64, (b.u64 & 63));
  216. }
  217. bool JMP_COND(uint8_t type, convertible_t& regb, int32_t imm32) {
  218. switch (type & 7)
  219. {
  220. case 0:
  221. return regb.u32 <= (uint32_t)imm32;
  222. case 1:
  223. return regb.u32 > (uint32_t)imm32;
  224. case 2:
  225. return safeSub(regb.i32, imm32) < 0;
  226. case 3:
  227. return safeSub(regb.i32, imm32) >= 0;
  228. case 4:
  229. return subOverflow(regb.i32, imm32);
  230. case 5:
  231. return !subOverflow(regb.i32, imm32);
  232. case 6:
  233. return regb.i32 < imm32;
  234. case 7:
  235. return regb.i32 >= imm32;
  236. }
  237. }
  238. void FPINIT() {
  239. #ifdef __SSE2__
  240. _mm_setcsr(0x9FC0); //Flush to zero, denormals are zero, default rounding mode, all exceptions disabled
  241. #else
  242. setRoundMode(FE_TONEAREST);
  243. #endif
  244. }
  245. void FPADD(convertible_t& a, fpu_reg_t& b, fpu_reg_t& c) {
  246. #ifdef __SSE2__
  247. __m128i ai = _mm_loadl_epi64((const __m128i*)&a);
  248. __m128d ad = _mm_cvtepi32_pd(ai);
  249. __m128d bd = _mm_load_pd(&b.lo.f64);
  250. __m128d cd = _mm_add_pd(ad, bd);
  251. _mm_store_pd(&c.lo.f64, cd);
  252. #else
  253. double alo = (double)a.i32lo;
  254. double ahi = (double)a.i32hi;
  255. c.lo.f64 = alo + b.lo.f64;
  256. c.hi.f64 = ahi + b.hi.f64;
  257. #endif
  258. }
  259. void FPSUB(convertible_t& a, fpu_reg_t& b, fpu_reg_t& c) {
  260. #ifdef __SSE2__
  261. __m128i ai = _mm_loadl_epi64((const __m128i*)&a);
  262. __m128d ad = _mm_cvtepi32_pd(ai);
  263. __m128d bd = _mm_load_pd(&b.lo.f64);
  264. __m128d cd = _mm_sub_pd(ad, bd);
  265. _mm_store_pd(&c.lo.f64, cd);
  266. #else
  267. double alo = (double)a.i32lo;
  268. double ahi = (double)a.i32hi;
  269. c.lo.f64 = alo - b.lo.f64;
  270. c.hi.f64 = ahi - b.hi.f64;
  271. #endif
  272. }
  273. void FPMUL(convertible_t& a, fpu_reg_t& b, fpu_reg_t& c) {
  274. #ifdef __SSE2__
  275. __m128i ai = _mm_loadl_epi64((const __m128i*)&a);
  276. __m128d ad = _mm_cvtepi32_pd(ai);
  277. __m128d bd = _mm_load_pd(&b.lo.f64);
  278. __m128d cd = _mm_mul_pd(ad, bd);
  279. __m128d mask = _mm_cmpeq_pd(cd, cd);
  280. cd = _mm_and_pd(cd, mask);
  281. _mm_store_pd(&c.lo.f64, cd);
  282. #else
  283. double alo = (double)a.i32lo;
  284. double ahi = (double)a.i32hi;
  285. c.lo.f64 = FlushNaN(alo * b.lo.f64);
  286. c.hi.f64 = FlushNaN(ahi * b.hi.f64);
  287. #endif
  288. }
  289. void FPDIV(convertible_t& a, fpu_reg_t& b, fpu_reg_t& c) {
  290. #ifdef __SSE2__
  291. __m128i ai = _mm_loadl_epi64((const __m128i*)&a);
  292. __m128d ad = _mm_cvtepi32_pd(ai);
  293. __m128d bd = _mm_load_pd(&b.lo.f64);
  294. __m128d cd = _mm_div_pd(ad, bd);
  295. __m128d mask = _mm_cmpeq_pd(cd, cd);
  296. cd = _mm_and_pd(cd, mask);
  297. _mm_store_pd(&c.lo.f64, cd);
  298. #else
  299. double alo = (double)a.i32lo;
  300. double ahi = (double)a.i32hi;
  301. c.lo.f64 = FlushDenormalNaN(alo / b.lo.f64);
  302. c.hi.f64 = FlushDenormalNaN(ahi / b.hi.f64);
  303. #endif
  304. }
  305. void FPSQRT(convertible_t& a, fpu_reg_t& b, fpu_reg_t& c) {
  306. #ifdef __SSE2__
  307. __m128i ai = _mm_loadl_epi64((const __m128i*)&a);
  308. __m128d ad = _mm_cvtepi32_pd(ai);
  309. const __m128d absmask = _mm_castsi128_pd(_mm_set1_epi64x(~(1LL << 63)));
  310. ad = _mm_and_pd(ad, absmask);
  311. __m128d cd = _mm_sqrt_pd(ad);
  312. _mm_store_pd(&c.lo.f64, cd);
  313. #else
  314. double alo = (double)a.i32lo;
  315. double ahi = (double)a.i32hi;
  316. c.lo.f64 = sqrt(std::abs(alo));
  317. c.hi.f64 = sqrt(std::abs(ahi));
  318. #endif
  319. }
  320. void FPROUND(convertible_t& a, fpu_reg_t& b, fpu_reg_t& c) {
  321. c.lo.f64 = convertSigned52(a.i64);
  322. switch (a.u64 & 3) {
  323. case RoundDown:
  324. #ifdef DEBUG
  325. std::cout << "Round FE_DOWNWARD (" << FE_DOWNWARD << ") = " <<
  326. #endif
  327. setRoundMode(FE_DOWNWARD);
  328. #ifdef DEBUG
  329. std::cout << std::endl;
  330. #endif
  331. break;
  332. case RoundUp:
  333. #ifdef DEBUG
  334. std::cout << "Round FE_UPWARD (" << FE_UPWARD << ") = " <<
  335. #endif
  336. setRoundMode(FE_UPWARD);
  337. #ifdef DEBUG
  338. std::cout << std::endl;
  339. #endif
  340. break;
  341. case RoundToZero:
  342. #ifdef DEBUG
  343. std::cout << "Round FE_TOWARDZERO (" << FE_TOWARDZERO << ") = " <<
  344. #endif
  345. setRoundMode(FE_TOWARDZERO);
  346. #ifdef DEBUG
  347. std::cout << std::endl;
  348. #endif
  349. break;
  350. default:
  351. #ifdef DEBUG
  352. std::cout << "Round FE_TONEAREST (" << FE_TONEAREST << ") = " <<
  353. #endif
  354. setRoundMode(FE_TONEAREST);
  355. #ifdef DEBUG
  356. std::cout << std::endl;
  357. #endif
  358. break;
  359. }
  360. }
  361. }
  362. }