instructionsPortable.cpp 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  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 "intrinPortable.h"
  17. #include "blake2/endian.h"
  18. #pragma STDC FENV_ACCESS on
  19. #include <cfenv>
  20. #include <cmath>
  21. #ifdef DEBUG
  22. #include <iostream>
  23. #endif
  24. #include "common.hpp"
  25. #if defined(__SIZEOF_INT128__)
  26. typedef unsigned __int128 uint128_t;
  27. typedef __int128 int128_t;
  28. uint64_t mulh(uint64_t a, uint64_t b) {
  29. return ((uint128_t)a * b) >> 64;
  30. }
  31. int64_t smulh(int64_t a, int64_t b) {
  32. return ((int128_t)a * b) >> 64;
  33. }
  34. #define HAVE_MULH
  35. #define HAVE_SMULH
  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. uint64_t rotl(uint64_t x, int c) {
  43. return _rotl64(x, c);
  44. }
  45. uint64_t rotr(uint64_t x , int c) {
  46. return _rotr64(x, c);
  47. }
  48. #define HAVE_ROTL
  49. #define HAVE_ROTR
  50. #if EVAL_DEFINE(__MACHINEARM64_X64(1))
  51. uint64_t mulh(uint64_t a, uint64_t b) {
  52. return __umulh(a, b);
  53. }
  54. #define HAVE_MULH
  55. #endif
  56. #if EVAL_DEFINE(__MACHINEX64(1))
  57. int64_t smulh(int64_t a, int64_t b) {
  58. int64_t hi;
  59. _mul128(a, b, &hi);
  60. return hi;
  61. }
  62. #define HAVE_SMULH
  63. #endif
  64. static void setRoundMode__(uint32_t mode) {
  65. _controlfp(mode, _MCW_RC);
  66. }
  67. #define HAVE_SETROUNDMODE_IMPL
  68. #endif
  69. #ifndef HAVE_SETROUNDMODE_IMPL
  70. static void setRoundMode__(uint32_t mode) {
  71. fesetround(mode);
  72. }
  73. #endif
  74. #ifndef HAVE_ROTR
  75. uint64_t rotr(uint64_t a, int b) {
  76. return (a >> b) | (a << (64 - b));
  77. }
  78. #define HAS_ROTR
  79. #endif
  80. #ifndef HAVE_ROTL
  81. uint64_t rotl(uint64_t a, int b) {
  82. return (a << b) | (a >> (64 - b));
  83. }
  84. #define HAS_ROTL
  85. #endif
  86. #ifndef HAVE_MULH
  87. #define LO(x) ((x)&0xffffffff)
  88. #define HI(x) ((x)>>32)
  89. uint64_t mulh(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 HAVE_MULH
  102. #endif
  103. #ifndef HAVE_SMULH
  104. int64_t smulh(int64_t a, int64_t b) {
  105. int64_t hi = mulh(a, b);
  106. if (a < 0LL) hi -= b;
  107. if (b < 0LL) hi -= a;
  108. return hi;
  109. }
  110. #define HAVE_SMULH
  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__(uint32_t a, uint32_t b) {
  123. int32_t temp;
  124. return __builtin_sub_overflow(unsigned32ToSigned2sCompl(a), unsigned32ToSigned2sCompl(b), &temp);
  125. }
  126. #define HAVE_SUB_OVERFLOW
  127. #endif
  128. #endif
  129. #ifndef HAVE_SUB_OVERFLOW
  130. static inline bool subOverflow__(uint32_t a, uint32_t b) {
  131. auto c = unsigned32ToSigned2sCompl(a - b);
  132. return (c < unsigned32ToSigned2sCompl(a)) != (unsigned32ToSigned2sCompl(b) > 0);
  133. }
  134. #define HAVE_SUB_OVERFLOW
  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. void setRoundMode(uint32_t rcflag) {
  147. switch (rcflag & 3) {
  148. case RoundDown:
  149. setRoundMode__(FE_DOWNWARD);
  150. break;
  151. case RoundUp:
  152. setRoundMode__(FE_UPWARD);
  153. break;
  154. case RoundToZero:
  155. setRoundMode__(FE_TOWARDZERO);
  156. break;
  157. case RoundToNearest:
  158. setRoundMode__(FE_TONEAREST);
  159. break;
  160. default:
  161. UNREACHABLE;
  162. }
  163. }
  164. bool condition(uint32_t type, uint32_t value, uint32_t imm32) {
  165. switch (type & 7)
  166. {
  167. case 0:
  168. return value <= imm32;
  169. case 1:
  170. return value > imm32;
  171. case 2:
  172. return unsigned32ToSigned2sCompl(value - imm32) < 0;
  173. case 3:
  174. return unsigned32ToSigned2sCompl(value - imm32) >= 0;
  175. case 4:
  176. return subOverflow__(value, imm32);
  177. case 5:
  178. return !subOverflow__(value, imm32);
  179. case 6:
  180. return unsigned32ToSigned2sCompl(value) < unsigned32ToSigned2sCompl(imm32);
  181. case 7:
  182. return unsigned32ToSigned2sCompl(value) >= unsigned32ToSigned2sCompl(imm32);
  183. default:
  184. UNREACHABLE;
  185. }
  186. }
  187. void initFpu() {
  188. #ifdef __SSE2__
  189. _mm_setcsr(0x9FC0); //Flush to zero, denormals are zero, default rounding mode, all exceptions disabled
  190. #else
  191. setRoundMode(FE_TONEAREST);
  192. #endif
  193. }
  194. union double_ser_t {
  195. double f;
  196. uint64_t i;
  197. };
  198. double loadDoublePortable(const void* addr) {
  199. double_ser_t ds;
  200. ds.i = load64(addr);
  201. return ds.f;
  202. }