instructionsPortable.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  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. //#define FTZ
  17. #include "instructions.hpp"
  18. #include "intrinPortable.h"
  19. #pragma STDC FENV_ACCESS on
  20. #include <cfenv>
  21. #include <cmath>
  22. #ifdef DEBUG
  23. #include <iostream>
  24. #endif
  25. #if defined(__SIZEOF_INT128__)
  26. typedef unsigned __int128 uint128_t;
  27. typedef __int128 int128_t;
  28. static inline uint64_t __umulhi64(uint64_t a, uint64_t b) {
  29. return ((uint128_t)a * b) >> 64;
  30. }
  31. static inline uint64_t __imulhi64(int64_t a, int64_t b) {
  32. return ((int128_t)a * b) >> 64;
  33. }
  34. #define umulhi64 __umulhi64
  35. #define imulhi64 __imulhi64
  36. #endif
  37. #if defined(_MSC_VER)
  38. #define HAS_VALUE(X) X ## 0
  39. #define EVAL_DEFINE(X) HAS_VALUE(X)
  40. #include <intrin.h>
  41. #include <stdlib.h>
  42. #define ror64 _rotr64
  43. #define rol64 _rotl64
  44. #if EVAL_DEFINE(__MACHINEARM64_X64(1))
  45. #define umulhi64 __umulh
  46. #endif
  47. #if EVAL_DEFINE(__MACHINEX64(1))
  48. static inline uint64_t __imulhi64(int64_t a, int64_t b) {
  49. int64_t hi;
  50. _mul128(a, b, &hi);
  51. return hi;
  52. }
  53. #define imulhi64 __imulhi64
  54. #endif
  55. static inline uint32_t _setRoundMode(uint32_t mode) {
  56. return _controlfp(mode, _MCW_RC);
  57. }
  58. #define setRoundMode _setRoundMode
  59. #endif
  60. #ifndef setRoundMode
  61. #define setRoundMode fesetround
  62. #endif
  63. #ifndef ror64
  64. static inline uint64_t __ror64(uint64_t a, int b) {
  65. return (a >> b) | (a << (64 - b));
  66. }
  67. #define ror64 __ror64
  68. #endif
  69. #ifndef rol64
  70. static inline uint64_t __rol64(uint64_t a, int b) {
  71. return (a << b) | (a >> (64 - b));
  72. }
  73. #define rol64 __rol64
  74. #endif
  75. #ifndef sar64
  76. #include <type_traits>
  77. constexpr int64_t builtintShr64(int64_t value, int shift) noexcept {
  78. return value >> shift;
  79. }
  80. struct UsesArithmeticShift : std::integral_constant<bool, builtintShr64(-1LL, 1) == -1LL> {
  81. };
  82. static inline int64_t __sar64(int64_t a, int b) {
  83. return UsesArithmeticShift::value ? builtintShr64(a, b) : (a < 0 ? ~(~a >> b) : a >> b);
  84. }
  85. #define sar64 __sar64
  86. #endif
  87. #ifndef umulhi64
  88. #define LO(x) ((x)&0xffffffff)
  89. #define HI(x) ((x)>>32)
  90. static inline uint64_t __umulhi64(uint64_t a, uint64_t b) {
  91. uint64_t ah = HI(a), al = LO(a);
  92. uint64_t bh = HI(b), bl = LO(b);
  93. uint64_t x00 = al * bl;
  94. uint64_t x01 = al * bh;
  95. uint64_t x10 = ah * bl;
  96. uint64_t x11 = ah * bh;
  97. uint64_t m1 = LO(x10) + LO(x01) + HI(x00);
  98. uint64_t m2 = HI(x10) + HI(x01) + LO(x11) + HI(m1);
  99. uint64_t m3 = HI(x11) + HI(m2);
  100. return (m3 << 32) + LO(m2);
  101. }
  102. #define umulhi64 __umulhi64
  103. #endif
  104. #ifndef imulhi64
  105. static inline int64_t __imulhi64(int64_t a, int64_t b) {
  106. int64_t hi = umulhi64(a, b);
  107. if (a < 0LL) hi -= b;
  108. if (b < 0LL) hi -= a;
  109. return hi;
  110. }
  111. #define imulhi64 __imulhi64
  112. #endif
  113. static double FlushDenormal(double x) {
  114. if (std::fpclassify(x) == FP_SUBNORMAL) {
  115. return 0;
  116. }
  117. return x;
  118. }
  119. #ifdef FTZ
  120. #undef FTZ
  121. #define FTZ(x) FlushDenormal(x)
  122. #else
  123. #define FTZ(x) x
  124. #endif
  125. namespace RandomX {
  126. extern "C" {
  127. void ADD_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  128. c.u64 = a.u64 + b.u64;
  129. }
  130. void ADD_32(convertible_t& a, convertible_t& b, convertible_t& c) {
  131. c.u64 = a.u32 + b.u32;
  132. }
  133. void SUB_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  134. c.u64 = a.u64 - b.u64;
  135. }
  136. void SUB_32(convertible_t& a, convertible_t& b, convertible_t& c) {
  137. c.u64 = a.u32 - b.u32;
  138. }
  139. void MUL_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  140. c.u64 = a.u64 * b.u64;
  141. }
  142. void MULH_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  143. c.u64 = umulhi64(a.u64, b.u64);
  144. }
  145. void MUL_32(convertible_t& a, convertible_t& b, convertible_t& c) {
  146. c.u64 = (uint64_t)a.u32 * b.u32;
  147. }
  148. void IMUL_32(convertible_t& a, convertible_t& b, convertible_t& c) {
  149. c.i64 = (int64_t)a.i32 * b.i32;
  150. }
  151. void IMULH_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  152. c.i64 = imulhi64(a.i64, b.i64);
  153. }
  154. void DIV_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  155. c.u64 = a.u64 / (b.u32 != 0 ? b.u32 : 1U);
  156. }
  157. void IDIV_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  158. if (a.i64 == INT64_MIN && b.i32 == -1)
  159. c.i64 = INT64_MIN;
  160. else
  161. c.i64 = a.i64 / (b.i32 != 0 ? b.i32 : 1);
  162. }
  163. void AND_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  164. c.u64 = a.u64 & b.u64;
  165. }
  166. void AND_32(convertible_t& a, convertible_t& b, convertible_t& c) {
  167. c.u64 = a.u32 & b.u32;
  168. }
  169. void OR_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  170. c.u64 = a.u64 | b.u64;
  171. }
  172. void OR_32(convertible_t& a, convertible_t& b, convertible_t& c) {
  173. c.u64 = a.u32 | b.u32;
  174. }
  175. void XOR_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  176. c.u64 = a.u64 ^ b.u64;
  177. }
  178. void XOR_32(convertible_t& a, convertible_t& b, convertible_t& c) {
  179. c.u64 = a.u32 ^ b.u32;
  180. }
  181. void SHL_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  182. c.u64 = a.u64 << (b.u64 & 63);
  183. }
  184. void SHR_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  185. c.u64 = a.u64 >> (b.u64 & 63);
  186. }
  187. void SAR_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  188. c.u64 = sar64(a.i64, b.u64 & 63);
  189. }
  190. void ROL_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  191. c.u64 = rol64(a.u64, (b.u64 & 63));
  192. }
  193. void ROR_64(convertible_t& a, convertible_t& b, convertible_t& c) {
  194. c.u64 = ror64(a.u64, (b.u64 & 63));
  195. }
  196. void FPINIT() {
  197. setRoundMode(FE_TONEAREST);
  198. }
  199. void FPADD(convertible_t& a, double b, convertible_t& c) {
  200. c.f64 = FTZ(convertToDouble(a.i64) + b);
  201. }
  202. void FPSUB(convertible_t& a, double b, convertible_t& c) {
  203. c.f64 = FTZ(convertToDouble(a.i64) - b);
  204. }
  205. void FPMUL(convertible_t& a, double b, convertible_t& c) {
  206. c.f64 = FTZ(convertToDoubleNonZero(a.i64) * b);
  207. }
  208. void FPDIV(convertible_t& a, double b, convertible_t& c) {
  209. c.f64 = FTZ(convertToDoubleNonZero(a.i64) / b);
  210. }
  211. void FPSQRT(convertible_t& a, convertible_t& b, convertible_t& c) {
  212. #ifdef __SSE2__
  213. double d = convertToDoubleNonNegative(a.i64);
  214. c.f64 = _mm_cvtsd_f64(_mm_sqrt_sd(_mm_setzero_pd(), _mm_load_pd(&d)));
  215. #else
  216. c.f64 = FTZ(sqrt(convertToDoubleNonNegative(a.i64)));
  217. #endif
  218. }
  219. void FPROUND(convertible_t& a, convertible_t& b, convertible_t& c) {
  220. c.f64 = convertToDouble(a.i64);
  221. switch (a.u64 & 3) {
  222. case RoundDown:
  223. #ifdef DEBUG
  224. std::cout << "Round FE_DOWNWARD (" << FE_DOWNWARD << ") = " <<
  225. #endif
  226. setRoundMode(FE_DOWNWARD);
  227. #ifdef DEBUG
  228. std::cout << std::endl;
  229. #endif
  230. break;
  231. case RoundUp:
  232. #ifdef DEBUG
  233. std::cout << "Round FE_UPWARD (" << FE_UPWARD << ") = " <<
  234. #endif
  235. setRoundMode(FE_UPWARD);
  236. #ifdef DEBUG
  237. std::cout << std::endl;
  238. #endif
  239. break;
  240. case RoundToZero:
  241. #ifdef DEBUG
  242. std::cout << "Round FE_TOWARDZERO (" << FE_TOWARDZERO << ") = " <<
  243. #endif
  244. setRoundMode(FE_TOWARDZERO);
  245. #ifdef DEBUG
  246. std::cout << std::endl;
  247. #endif
  248. break;
  249. default:
  250. #ifdef DEBUG
  251. std::cout << "Round FE_TONEAREST (" << FE_TONEAREST << ") = " <<
  252. #endif
  253. setRoundMode(FE_TONEAREST);
  254. #ifdef DEBUG
  255. std::cout << std::endl;
  256. #endif
  257. break;
  258. }
  259. }
  260. }
  261. }