TestAluFpu.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  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. #include <iostream>
  16. #include <iomanip>
  17. #include <limits>
  18. #include "instructions.hpp"
  19. #include "Pcg32.hpp"
  20. //#define DEBUG
  21. using namespace RandomX;
  22. typedef void(*VmOperation)(convertible_t&, convertible_t&, convertible_t&);
  23. uint64_t rxRound(uint32_t mode, int64_t x, int64_t y, VmOperation op) {
  24. convertible_t a, b, c;
  25. a.u64 = mode;
  26. FPROUND(a, b, c);
  27. #ifdef DEBUG
  28. a.f64 = convertToDouble(x);
  29. b.f64 = convertToDouble(y);
  30. std::cout << std::hex << (uint64_t)x << " -> " << a.u64 << std::endl;
  31. std::cout << std::hex << (uint64_t)y << " -> " << b.u64 << std::endl;
  32. std::cout << std::dec;
  33. #endif
  34. a.i64 = x;
  35. b.i64 = y;
  36. op(a, b, c);
  37. return c.u64;
  38. }
  39. #define CATCH_CONFIG_MAIN
  40. #include "catch.hpp"
  41. #define RX_EXECUTE_U64(va, vb, INST) do { \
  42. a.u64 = va; \
  43. b.u64 = vb; \
  44. INST(a, b, c); \
  45. } while(false)
  46. #define RX_EXECUTE_I64(va, vb, INST) do { \
  47. a.i64 = va; \
  48. b.i64 = vb; \
  49. INST(a, b, c); \
  50. } while(false)
  51. TEST_CASE("Integer addition (64-bit)", "[ADD_64]") {
  52. convertible_t a, b, c;
  53. RX_EXECUTE_U64(0xFFFFFFFF, 0x1, ADD_64);
  54. CHECK(c.u64 == 0x100000000);
  55. RX_EXECUTE_U64(0x8000000000000000, 0x8000000000000000, ADD_64);
  56. CHECK(c.u64 == 0x0);
  57. }
  58. TEST_CASE("Integer addition (32-bit)", "[ADD_32]") {
  59. convertible_t a, b, c;
  60. RX_EXECUTE_U64(0xFFFFFFFF, 0x1, ADD_32);
  61. CHECK(c.u64 == 0);
  62. RX_EXECUTE_U64(0xFF00000000000001, 0x0000000100000001, ADD_32);
  63. CHECK(c.u64 == 2);
  64. }
  65. TEST_CASE("Integer subtraction (64-bit)", "[SUB_64]") {
  66. convertible_t a, b, c;
  67. RX_EXECUTE_U64(1, 0xFFFFFFFF, SUB_64);
  68. CHECK(c.u64 == 0xFFFFFFFF00000002);
  69. }
  70. TEST_CASE("Integer subtraction (32-bit)", "[SUB_32]") {
  71. convertible_t a, b, c;
  72. RX_EXECUTE_U64(1, 0xFFFFFFFF, SUB_32);
  73. CHECK(c.u64 == 2);
  74. }
  75. TEST_CASE("Unsigned multiplication (64-bit, low half)", "[MUL_64]") {
  76. convertible_t a, b, c;
  77. RX_EXECUTE_U64(0xBC550E96BA88A72B, 0xF5391FA9F18D6273, MUL_64);
  78. CHECK(c.u64 == 0x28723424A9108E51);
  79. }
  80. TEST_CASE("Unsigned multiplication (64-bit, high half)", "[MULH_64]") {
  81. convertible_t a, b, c;
  82. RX_EXECUTE_U64(0xBC550E96BA88A72B, 0xF5391FA9F18D6273, MULH_64);
  83. CHECK(c.u64 == 0xB4676D31D2B34883);
  84. }
  85. TEST_CASE("Unsigned multiplication (32-bit x 32-bit -> 64-bit)", "[MUL_32]") {
  86. convertible_t a, b, c;
  87. RX_EXECUTE_U64(0xBC550E96BA88A72B, 0xF5391FA9F18D6273, MUL_32);
  88. CHECK(c.u64 == 0xB001AA5FA9108E51);
  89. }
  90. TEST_CASE("Signed multiplication (32-bit x 32-bit -> 64-bit)", "[IMUL_32]") {
  91. convertible_t a, b, c;
  92. RX_EXECUTE_U64(0xBC550E96BA88A72B, 0xF5391FA9F18D6273, IMUL_32);
  93. CHECK(c.u64 == 0x03EBA0C1A9108E51);
  94. }
  95. TEST_CASE("Signed multiplication (64-bit, high half)", "[IMULH_64]") {
  96. convertible_t a, b, c;
  97. RX_EXECUTE_U64(0xBC550E96BA88A72B, 0xF5391FA9F18D6273, IMULH_64);
  98. CHECK(c.u64 == 0x02D93EF1269D3EE5);
  99. }
  100. TEST_CASE("Unsigned division (64-bit / 32-bit -> 32-bit)", "[DIV_64]") {
  101. convertible_t a, b, c;
  102. RX_EXECUTE_U64(8774217225983458895, 3014068202, DIV_64);
  103. CHECK(c.u64 == 2911087818);
  104. RX_EXECUTE_U64(8774217225983458895, 0, DIV_64);
  105. CHECK(c.u64 == 8774217225983458895);
  106. RX_EXECUTE_U64(3014068202, 8774217225983458895, DIV_64);
  107. CHECK(c.u64 == 2);
  108. }
  109. TEST_CASE("Signed division (64-bit / 32-bit -> 32-bit)", "[IDIV_64]") {
  110. convertible_t a, b, c;
  111. RX_EXECUTE_U64(8774217225983458895, 3014068202, IDIV_64);
  112. CHECK(c.u64 == 0xFFFFFFFE67B4994E);
  113. RX_EXECUTE_U64(5, 0xFFFFFFFFFFFFFFFF, IDIV_64);
  114. CHECK(c.u64 == 0xFFFFFFFFFFFFFFFB);
  115. RX_EXECUTE_U64(8774217225983458895, 0, IDIV_64);
  116. CHECK(c.u64 == 8774217225983458895);
  117. RX_EXECUTE_U64(0x8000000000000000, 0xFFFFFFFFFFFFFFFF, IDIV_64);
  118. CHECK(c.u64 == 0x8000000000000000);
  119. RX_EXECUTE_U64(0x8000000000000000, 0x93D1FFFFFFFFFFFF, IDIV_64);
  120. CHECK(c.u64 == 0x8000000000000000);
  121. RX_EXECUTE_U64(0xFFFFFFFFB3A707EA, 8774217225983458895, IDIV_64);
  122. CHECK(c.u64 == 0xFFFFFFFFFFFFFFFF);
  123. }
  124. TEST_CASE("Bitwise AND (64-bit)", "[AND_64]") {
  125. convertible_t a, b, c;
  126. RX_EXECUTE_U64(0xCCCCCCCCCCCCCCCC, 0xAAAAAAAAAAAAAAAA, AND_64);
  127. CHECK(c.u64 == 0x8888888888888888);
  128. }
  129. TEST_CASE("Bitwise AND (32-bit)", "[AND_32]") {
  130. convertible_t a, b, c;
  131. RX_EXECUTE_U64(0xCCCCCCCCCCCCCCCC, 0xAAAAAAAAAAAAAAAA, AND_32);
  132. CHECK(c.u64 == 0x88888888);
  133. }
  134. TEST_CASE("Bitwise OR (64-bit)", "[OR_64]") {
  135. convertible_t a, b, c;
  136. RX_EXECUTE_U64(0x4444444444444444, 0xAAAAAAAAAAAAAAAA, OR_64);
  137. CHECK(c.u64 == 0xEEEEEEEEEEEEEEEE);
  138. }
  139. TEST_CASE("Bitwise OR (32-bit)", "[OR_32]") {
  140. convertible_t a, b, c;
  141. RX_EXECUTE_U64(0x4444444444444444, 0xAAAAAAAAAAAAAAAA, OR_32);
  142. CHECK(c.u64 == 0xEEEEEEEE);
  143. }
  144. TEST_CASE("Bitwise XOR (64-bit)", "[XOR_64]") {
  145. convertible_t a, b, c;
  146. RX_EXECUTE_U64(0x8888888888888888, 0xAAAAAAAAAAAAAAAA, XOR_64);
  147. CHECK(c.u64 == 0x2222222222222222);
  148. }
  149. TEST_CASE("Bitwise XOR (32-bit)", "[XOR_32]") {
  150. convertible_t a, b, c;
  151. RX_EXECUTE_U64(0x8888888888888888, 0xAAAAAAAAAAAAAAAA, XOR_32);
  152. CHECK(c.u64 == 0x22222222);
  153. }
  154. TEST_CASE("Logical left shift (64-bit)", "[SHL_64]") {
  155. convertible_t a, b, c;
  156. RX_EXECUTE_U64(0x3, 52, SHL_64);
  157. CHECK(c.u64 == 0x30000000000000);
  158. RX_EXECUTE_U64(953360005391419562, 4569451684712230561, SHL_64);
  159. CHECK(c.u64 == 6978065200108797952);
  160. RX_EXECUTE_U64(0x8000000000000000, 1, SHL_64);
  161. CHECK(c.u64 == 0);
  162. }
  163. TEST_CASE("Logical right shift (64-bit)", "[SHR_64]") {
  164. convertible_t a, b, c;
  165. RX_EXECUTE_U64(0x3, 52, SHR_64);
  166. CHECK(c.u64 == 0);
  167. RX_EXECUTE_U64(953360005391419562, 4569451684712230561, SHR_64);
  168. CHECK(c.u64 == 110985711);
  169. RX_EXECUTE_U64(0x8000000000000000, 1, SHR_64);
  170. CHECK(c.u64 == 0x4000000000000000);
  171. }
  172. TEST_CASE("Arithmetic right shift (64-bit)", "[SAR_64]") {
  173. convertible_t a, b, c;
  174. RX_EXECUTE_I64(-9, 2, SAR_64);
  175. CHECK(c.i64 == -3);
  176. RX_EXECUTE_I64(INT64_MIN, 63, SAR_64);
  177. CHECK(c.i64 == -1);
  178. RX_EXECUTE_I64(INT64_MAX, 163768499474606398, SAR_64);
  179. CHECK(c.i64 == 1);
  180. }
  181. TEST_CASE("Circular left shift (64-bit)", "[ROL_64]") {
  182. convertible_t a, b, c;
  183. RX_EXECUTE_U64(0x3, 52, ROL_64);
  184. CHECK(c.u64 == 0x30000000000000);
  185. RX_EXECUTE_U64(953360005391419562, 4569451684712230561, ROL_64);
  186. CHECK(c.u64 == 6978065200552740799);
  187. RX_EXECUTE_U64(0x8000000000000000, 1, ROL_64);
  188. CHECK(c.u64 == 1);
  189. }
  190. TEST_CASE("Circular right shift (64-bit)", "[ROR_64]") {
  191. convertible_t a, b, c;
  192. RX_EXECUTE_U64(0x3, 52, ROR_64);
  193. CHECK(c.u64 == 12288);
  194. RX_EXECUTE_U64(953360005391419562, 4569451684712230561, ROR_64);
  195. CHECK(c.u64 == 0xD835C455069D81EF);
  196. RX_EXECUTE_U64(0x8000000000000000, 1, ROR_64);
  197. CHECK(c.u64 == 0x4000000000000000);
  198. }
  199. TEST_CASE("Denormal results are not produced", "[FTZ]") {
  200. FPINIT();
  201. convertible_t a, b, c;
  202. a.i64 = 2048;
  203. FPDIV(a, DBL_MAX, c);
  204. #ifdef DEBUG
  205. std::cout << a.i64 << " / " << DBL_MAX << " = " << std::hex << c.u64 << std::endl;
  206. #endif
  207. REQUIRE(std::fpclassify(c.f64) != FP_SUBNORMAL);
  208. b.f64 = c.f64;
  209. a.i64 = 0;
  210. FPSUB_64(a, b, c);
  211. #ifdef DEBUG
  212. std::cout << a.i64 << " - " << b.f64 << " = " << std::hex << c.u64 << std::endl;
  213. #endif
  214. CHECK(std::fpclassify(c.f64) != FP_SUBNORMAL);
  215. }
  216. TEST_CASE("NaN results are not produced", "[NAN]") {
  217. FPINIT();
  218. convertible_t a, c;
  219. a.i64 = 0;
  220. FPDIV(a, 0, c);
  221. CHECK(std::fpclassify(c.f64) != FP_NAN);
  222. FPMUL(a, std::numeric_limits<double>::infinity(), c);
  223. CHECK(std::fpclassify(c.f64) != FP_NAN);
  224. }
  225. volatile int64_t fpAdda = 7379480244170225589;
  226. volatile int64_t fpAddb = -438072579179686797;
  227. volatile int64_t fpSuba = 2939258788088626026;
  228. volatile int64_t fpSubb = 4786131045320678734;
  229. volatile int64_t fpMula1 = 8399833736388895639;
  230. volatile int64_t fpMulb1 = 5671608020317594922;
  231. volatile int64_t fpMula2 = -7094299423744805450;
  232. volatile int64_t fpMulb2 = 4982086006202596504;
  233. volatile int64_t fpDiva1 = 8399833736388895639;
  234. volatile int64_t fpDivb1 = 5671608020317594922;
  235. volatile int64_t fpDiva2 = -7434878587645025912;
  236. volatile int64_t fpDivb2 = 5266243837734830806;
  237. volatile int64_t fpSqrta = -7594301562963134542;
  238. TEST_CASE("IEEE-754 compliance", "[FPU]") {
  239. FPINIT();
  240. convertible_t a, b, c;
  241. a.i64 = 2048;
  242. FPDIV(a, 0, c);
  243. CHECK(c.f64 == std::numeric_limits<double>::infinity());
  244. a.i64 = -2048;
  245. FPDIV(a, 0, c);
  246. CHECK(c.f64 == -std::numeric_limits<double>::infinity());
  247. #ifdef DEBUG
  248. std::cout << "FPROUND" << std::endl;
  249. #endif
  250. CHECK(rxRound(RoundToNearest, fpAdda, 0, &FPROUND) == 0x43d99a4b8bc531dcU);
  251. CHECK(rxRound(RoundDown, fpAdda, 0, &FPROUND) == 0x43d99a4b8bc531dcU);
  252. CHECK(rxRound(RoundUp, fpAdda, 0, &FPROUND) == 0x43d99a4b8bc531dcU);
  253. CHECK(rxRound(RoundToZero, fpAdda, 0, &FPROUND) == 0x43d99a4b8bc531dcU);
  254. CHECK(rxRound(RoundToNearest, fpSuba, 0, &FPROUND) == 0x43c4652c25bf7bdcU);
  255. CHECK(rxRound(RoundDown, fpSuba, 0, &FPROUND) == 0x43c4652c25bf7bdcU);
  256. CHECK(rxRound(RoundUp, fpSuba, 0, &FPROUND) == 0x43c4652c25bf7bdcU);
  257. CHECK(rxRound(RoundToZero, fpSuba, 0, &FPROUND) == 0x43c4652c25bf7bdcU);
  258. #ifdef DEBUG
  259. std::cout << "FPADD" << std::endl;
  260. #endif
  261. CHECK(rxRound(RoundToNearest, fpAdda, fpAddb, &FPADD_64) == 0xf9eba74f6c27d473U);
  262. CHECK(rxRound(RoundDown, fpAdda, fpAddb, &FPADD_64) == 0xf9eba74f6c27d473U);
  263. CHECK(rxRound(RoundUp, fpAdda, fpAddb, &FPADD_64) == 0xf9eba74f6c27d472U);
  264. CHECK(rxRound(RoundToZero, fpAdda, fpAddb, &FPADD_64) == 0xf9eba74f6c27d472U);
  265. #ifdef DEBUG
  266. std::cout << "FPSUB" << std::endl;
  267. #endif
  268. CHECK(rxRound(RoundToNearest, fpSuba, fpSubb, &FPSUB_64) == 0x43c4652bb6bc2c49U);
  269. CHECK(rxRound(RoundDown, fpSuba, fpSubb, &FPSUB_64) == 0x43c4652bb6bc2c48U);
  270. CHECK(rxRound(RoundUp, fpSuba, fpSubb, &FPSUB_64) == 0x43c4652bb6bc2c49U);
  271. CHECK(rxRound(RoundToZero, fpSuba, fpSubb, &FPSUB_64) == 0x43c4652bb6bc2c48U);
  272. #ifdef DEBUG
  273. std::cout << "FPMUL" << std::endl;
  274. #endif
  275. CHECK(rxRound(RoundToNearest, fpMula1, fpMulb1, &FPMUL_64) == 0x52a3abbb1677f3e9U);
  276. CHECK(rxRound(RoundDown, fpMula1, fpMulb1, &FPMUL_64) == 0x52a3abbb1677f3e8U);
  277. CHECK(rxRound(RoundUp, fpMula1, fpMulb1, &FPMUL_64) == 0x52a3abbb1677f3e9U);
  278. CHECK(rxRound(RoundToZero, fpMula1, fpMulb1, &FPMUL_64) == 0x52a3abbb1677f3e8U);
  279. CHECK(rxRound(RoundToNearest, fpMula2, fpMulb2, &FPMUL_64) == 0xc90ea6c25e29c583U);
  280. CHECK(rxRound(RoundDown, fpMula2, fpMulb2, &FPMUL_64) == 0xc90ea6c25e29c583U);
  281. CHECK(rxRound(RoundUp, fpMula2, fpMulb2, &FPMUL_64) == 0xc90ea6c25e29c582U);
  282. CHECK(rxRound(RoundToZero, fpMula2, fpMulb2, &FPMUL_64) == 0xc90ea6c25e29c582U);
  283. #ifdef DEBUG
  284. std::cout << "FPDIV" << std::endl;
  285. #endif
  286. CHECK(rxRound(RoundToNearest, fpDiva1, fpDivb1, &FPDIV_64) == 0x3515967d3015e81cU);
  287. CHECK(rxRound(RoundDown, fpDiva1, fpDivb1, &FPDIV_64) == 0x3515967d3015e81bU);
  288. CHECK(rxRound(RoundUp, fpDiva1, fpDivb1, &FPDIV_64) == 0x3515967d3015e81cU);
  289. CHECK(rxRound(RoundToZero, fpDiva1, fpDivb1, &FPDIV_64) == 0x3515967d3015e81bU);
  290. CHECK(rxRound(RoundToNearest, fpDiva2, fpDivb2, &FPDIV_64) == 0xbab33c30b92b8fccU);
  291. CHECK(rxRound(RoundDown, fpDiva2, fpDivb2, &FPDIV_64) == 0xbab33c30b92b8fccU);
  292. CHECK(rxRound(RoundUp, fpDiva2, fpDivb2, &FPDIV_64) == 0xbab33c30b92b8fcbU);
  293. CHECK(rxRound(RoundToZero, fpDiva2, fpDivb2, &FPDIV_64) == 0xbab33c30b92b8fcbU);
  294. #ifdef DEBUG
  295. std::cout << "FPSQRT" << std::endl;
  296. #endif
  297. CHECK(rxRound(RoundToNearest, fpSqrta, 0, &FPSQRT) == 0x41d304e3fcc31a2dU);
  298. CHECK(rxRound(RoundDown, fpSqrta, 0, &FPSQRT) == 0x41d304e3fcc31a2cU);
  299. CHECK(rxRound(RoundUp, fpSqrta, 0, &FPSQRT) == 0x41d304e3fcc31a2dU);
  300. CHECK(rxRound(RoundToZero, fpSqrta, 0, &FPSQRT) == 0x41d304e3fcc31a2cU);
  301. }