InterpretedVirtualMachine.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986
  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 TRACE
  16. //#define FPUCHECK
  17. #define RANDOMX_JUMP
  18. #include "InterpretedVirtualMachine.hpp"
  19. #include "dataset.hpp"
  20. #include "Cache.hpp"
  21. #include "LightClientAsyncWorker.hpp"
  22. #include <iostream>
  23. #include <iomanip>
  24. #include <stdexcept>
  25. #include <sstream>
  26. #include <cmath>
  27. #include <cfloat>
  28. #include <thread>
  29. #include <climits>
  30. #include "intrinPortable.h"
  31. #include "reciprocal.h"
  32. #ifdef STATS
  33. #include <algorithm>
  34. #endif
  35. #include "LightProgramGenerator.hpp"
  36. #ifdef FPUCHECK
  37. constexpr bool fpuCheck = true;
  38. #else
  39. constexpr bool fpuCheck = false;
  40. #endif
  41. namespace RandomX {
  42. template<bool superscalar>
  43. void InterpretedVirtualMachine<superscalar>::setDataset(dataset_t ds, uint64_t size, LightProgram(&programs)[RANDOMX_CACHE_ACCESSES]) {
  44. mem.ds = ds;
  45. readDataset = &datasetReadLight;
  46. datasetRange = (size - RANDOMX_DATASET_SIZE + CacheLineSize) / CacheLineSize;
  47. if(superscalar)
  48. precompileSuperscalar(programs);
  49. }
  50. template void InterpretedVirtualMachine<true>::setDataset(dataset_t ds, uint64_t size, LightProgram(&programs)[RANDOMX_CACHE_ACCESSES]);
  51. template void InterpretedVirtualMachine<false>::setDataset(dataset_t ds, uint64_t size, LightProgram(&programs)[RANDOMX_CACHE_ACCESSES]);
  52. template<bool superscalar>
  53. void InterpretedVirtualMachine<superscalar>::initialize() {
  54. VirtualMachine::initialize();
  55. for (unsigned i = 0; i < RANDOMX_PROGRAM_SIZE; ++i) {
  56. program(i).src %= RegistersCount;
  57. program(i).dst %= RegistersCount;
  58. }
  59. }
  60. template void InterpretedVirtualMachine<true>::initialize();
  61. template void InterpretedVirtualMachine<false>::initialize();
  62. template<bool superscalar>
  63. void InterpretedVirtualMachine<superscalar>::executeBytecode(int_reg_t(&r)[8], __m128d (&f)[4], __m128d (&e)[4], __m128d (&a)[4]) {
  64. for (int ic = 0; ic < RANDOMX_PROGRAM_SIZE; ++ic) {
  65. executeBytecode(ic, r, f, e, a);
  66. }
  67. }
  68. template void InterpretedVirtualMachine<true>::executeBytecode(int_reg_t(&r)[8], __m128d (&f)[4], __m128d (&e)[4], __m128d (&a)[4]);
  69. template void InterpretedVirtualMachine<false>::executeBytecode(int_reg_t(&r)[8], __m128d (&f)[4], __m128d (&e)[4], __m128d (&a)[4]);
  70. static void print(int_reg_t r) {
  71. std::cout << std::hex << std::setw(16) << std::setfill('0') << r << std::endl;
  72. }
  73. static void print(__m128d f) {
  74. uint64_t lo = *(((uint64_t*)&f) + 0);
  75. uint64_t hi = *(((uint64_t*)&f) + 1);
  76. std::cout << std::hex << std::setw(16) << std::setfill('0') << hi << '-' << std::hex << std::setw(16) << std::setfill('0') << lo << std::endl;
  77. }
  78. static void printState(int_reg_t(&r)[8], __m128d (&f)[4], __m128d (&e)[4], __m128d (&a)[4]) {
  79. for (int i = 0; i < 8; ++i) {
  80. std::cout << "r" << i << " = "; print(r[i]);
  81. }
  82. for (int i = 0; i < 4; ++i) {
  83. std::cout << "f" << i << " = "; print(f[i]);
  84. }
  85. for (int i = 0; i < 4; ++i) {
  86. std::cout << "e" << i << " = "; print(e[i]);
  87. }
  88. for (int i = 0; i < 4; ++i) {
  89. std::cout << "a" << i << " = "; print(a[i]);
  90. }
  91. }
  92. static bool isDenormal(double x) {
  93. return std::fpclassify(x) == FP_SUBNORMAL;
  94. }
  95. template<bool superscalar>
  96. FORCE_INLINE void InterpretedVirtualMachine<superscalar>::executeBytecode(int& ic, int_reg_t(&r)[8], __m128d (&f)[4], __m128d (&e)[4], __m128d (&a)[4]) {
  97. auto& ibc = byteCode[ic];
  98. if (trace) std::cout << std::dec << std::setw(3) << ic << " " << program(ic);
  99. //if(trace) printState(r, f, e, a);
  100. switch (ibc.type)
  101. {
  102. case InstructionType::IADD_RS: {
  103. *ibc.idst += (*ibc.isrc << ibc.shift) + ibc.imm;
  104. } break;
  105. case InstructionType::IADD_M: {
  106. *ibc.idst += load64(scratchpad + (*ibc.isrc & ibc.memMask));
  107. } break;
  108. case InstructionType::IADD_RC: {
  109. *ibc.idst += *ibc.isrc + ibc.imm;
  110. } break;
  111. case InstructionType::ISUB_R: {
  112. *ibc.idst -= *ibc.isrc;
  113. } break;
  114. case InstructionType::ISUB_M: {
  115. *ibc.idst -= load64(scratchpad + (*ibc.isrc & ibc.memMask));
  116. } break;
  117. case InstructionType::IMUL_9C: {
  118. *ibc.idst += 8 * *ibc.idst + ibc.imm;
  119. } break;
  120. case InstructionType::IMUL_R: { //also handles IMUL_RCP
  121. *ibc.idst *= *ibc.isrc;
  122. } break;
  123. case InstructionType::IMUL_M: {
  124. *ibc.idst *= load64(scratchpad + (*ibc.isrc & ibc.memMask));
  125. } break;
  126. case InstructionType::IMULH_R: {
  127. *ibc.idst = mulh(*ibc.idst, *ibc.isrc);
  128. } break;
  129. case InstructionType::IMULH_M: {
  130. *ibc.idst = mulh(*ibc.idst, load64(scratchpad + (*ibc.isrc & ibc.memMask)));
  131. } break;
  132. case InstructionType::ISMULH_R: {
  133. *ibc.idst = smulh(unsigned64ToSigned2sCompl(*ibc.idst), unsigned64ToSigned2sCompl(*ibc.isrc));
  134. } break;
  135. case InstructionType::ISMULH_M: {
  136. *ibc.idst = smulh(unsigned64ToSigned2sCompl(*ibc.idst), unsigned64ToSigned2sCompl(load64(scratchpad + (*ibc.isrc & ibc.memMask))));
  137. } break;
  138. case InstructionType::INEG_R: {
  139. *ibc.idst = ~(*ibc.idst) + 1; //two's complement negative
  140. } break;
  141. case InstructionType::IXOR_R: {
  142. *ibc.idst ^= *ibc.isrc;
  143. } break;
  144. case InstructionType::IXOR_M: {
  145. *ibc.idst ^= load64(scratchpad + (*ibc.isrc & ibc.memMask));
  146. } break;
  147. case InstructionType::IROR_R: {
  148. *ibc.idst = rotr(*ibc.idst, *ibc.isrc & 63);
  149. } break;
  150. case InstructionType::IROL_R: {
  151. *ibc.idst = rotl(*ibc.idst, *ibc.isrc & 63);
  152. } break;
  153. case InstructionType::ISWAP_R: {
  154. int_reg_t temp = *ibc.isrc;
  155. *ibc.isrc = *ibc.idst;
  156. *ibc.idst = temp;
  157. } break;
  158. case InstructionType::FSWAP_R: {
  159. *ibc.fdst = _mm_shuffle_pd(*ibc.fdst, *ibc.fdst, 1);
  160. } break;
  161. case InstructionType::FADD_R: {
  162. *ibc.fdst = _mm_add_pd(*ibc.fdst, *ibc.fsrc);
  163. } break;
  164. case InstructionType::FADD_M: {
  165. __m128d fsrc = load_cvt_i32x2(scratchpad + (*ibc.isrc & ibc.memMask));
  166. *ibc.fdst = _mm_add_pd(*ibc.fdst, fsrc);
  167. } break;
  168. case InstructionType::FSUB_R: {
  169. *ibc.fdst = _mm_sub_pd(*ibc.fdst, *ibc.fsrc);
  170. } break;
  171. case InstructionType::FSUB_M: {
  172. __m128d fsrc = load_cvt_i32x2(scratchpad + (*ibc.isrc & ibc.memMask));
  173. *ibc.fdst = _mm_sub_pd(*ibc.fdst, fsrc);
  174. } break;
  175. case InstructionType::FSCAL_R: {
  176. const __m128d mask = _mm_castsi128_pd(_mm_set1_epi64x(0x81F0000000000000));
  177. *ibc.fdst = _mm_xor_pd(*ibc.fdst, mask);
  178. } break;
  179. case InstructionType::FMUL_R: {
  180. *ibc.fdst = _mm_mul_pd(*ibc.fdst, *ibc.fsrc);
  181. } break;
  182. case InstructionType::FDIV_M: {
  183. __m128d fsrc = ieee_set_exponent<-240>(load_cvt_i32x2(scratchpad + (*ibc.isrc & ibc.memMask)));
  184. *ibc.fdst = _mm_div_pd(*ibc.fdst, fsrc);
  185. } break;
  186. case InstructionType::FSQRT_R: {
  187. *ibc.fdst = _mm_sqrt_pd(*ibc.fdst);
  188. } break;
  189. case InstructionType::COND_R: {
  190. #ifdef RANDOMX_JUMP
  191. *ibc.creg += (1 << ibc.shift);
  192. const uint64_t conditionMask = ((1ULL << RANDOMX_CONDITION_BITS) - 1) << ibc.shift;
  193. if ((*ibc.creg & conditionMask) == 0) {
  194. #ifdef STATS
  195. count_JUMP_taken++;
  196. #endif
  197. ic = ibc.target;
  198. break;
  199. }
  200. #ifdef STATS
  201. count_JUMP_not_taken++;
  202. #endif
  203. #endif
  204. *ibc.idst += condition(ibc.condition, *ibc.isrc, ibc.imm) ? 1 : 0;
  205. } break;
  206. case InstructionType::COND_M: {
  207. #ifdef RANDOMX_JUMP
  208. *ibc.creg += (1uLL << ibc.shift);
  209. const uint64_t conditionMask = ((1ULL << RANDOMX_CONDITION_BITS) - 1) << ibc.shift;
  210. if ((*ibc.creg & conditionMask) == 0) {
  211. #ifdef STATS
  212. count_JUMP_taken++;
  213. #endif
  214. ic = ibc.target;
  215. break;
  216. }
  217. #ifdef STATS
  218. count_JUMP_not_taken++;
  219. #endif
  220. #endif
  221. *ibc.idst += condition(ibc.condition, load64(scratchpad + (*ibc.isrc & ibc.memMask)), ibc.imm) ? 1 : 0;
  222. } break;
  223. case InstructionType::CFROUND: {
  224. setRoundMode(rotr(*ibc.isrc, ibc.imm) % 4);
  225. } break;
  226. case InstructionType::ISTORE: {
  227. store64(scratchpad + (*ibc.idst & ibc.memMask), *ibc.isrc);
  228. } break;
  229. case InstructionType::NOP: {
  230. //nothing
  231. } break;
  232. default:
  233. UNREACHABLE;
  234. }
  235. if (trace) {
  236. if(ibc.type < 20 || ibc.type == 31 || ibc.type == 32)
  237. print(*ibc.idst);
  238. else //if(ibc.type >= 20 && ibc.type <= 30)
  239. print(0);
  240. }
  241. #ifdef FPUCHECK
  242. if (ibc.type >= 26 && ibc.type <= 30) {
  243. double lo = *(((double*)ibc.fdst) + 0);
  244. double hi = *(((double*)ibc.fdst) + 1);
  245. if (lo <= 0 || hi <= 0) {
  246. std::stringstream ss;
  247. ss << "Underflow in operation " << ibc.type;
  248. printState(r, f, e, a);
  249. throw std::runtime_error(ss.str());
  250. }
  251. }
  252. #endif
  253. }
  254. template<bool superscalar>
  255. void InterpretedVirtualMachine<superscalar>::execute() {
  256. int_reg_t r[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
  257. __m128d f[4];
  258. __m128d e[4];
  259. __m128d a[4];
  260. a[0] = _mm_load_pd(&reg.a[0].lo);
  261. a[1] = _mm_load_pd(&reg.a[1].lo);
  262. a[2] = _mm_load_pd(&reg.a[2].lo);
  263. a[3] = _mm_load_pd(&reg.a[3].lo);
  264. precompileProgram(r, f, e, a);
  265. uint32_t spAddr0 = mem.mx;
  266. uint32_t spAddr1 = mem.ma;
  267. if (trace) {
  268. std::cout << "execute (reg: r" << readReg0 << ", r" << readReg1 << ", r" << readReg2 << ", r" << readReg3 << ")" << std::endl;
  269. std::cout << "spAddr " << std::hex << std::setw(8) << std::setfill('0') << spAddr1 << " / " << std::setw(8) << std::setfill('0') << spAddr0 << std::endl;
  270. std::cout << "ma/mx " << std::hex << std::setw(8) << std::setfill('0') << mem.ma << std::setw(8) << std::setfill('0') << mem.mx << std::endl;
  271. printState(r, f, e, a);
  272. }
  273. for(unsigned ic = 0; ic < RANDOMX_PROGRAM_ITERATIONS; ++ic) {
  274. //std::cout << "Iteration " << iter << std::endl;
  275. uint64_t spMix = r[readReg0] ^ r[readReg1];
  276. spAddr0 ^= spMix;
  277. spAddr0 &= ScratchpadL3Mask64;
  278. spAddr1 ^= spMix >> 32;
  279. spAddr1 &= ScratchpadL3Mask64;
  280. r[0] ^= load64(scratchpad + spAddr0 + 0);
  281. r[1] ^= load64(scratchpad + spAddr0 + 8);
  282. r[2] ^= load64(scratchpad + spAddr0 + 16);
  283. r[3] ^= load64(scratchpad + spAddr0 + 24);
  284. r[4] ^= load64(scratchpad + spAddr0 + 32);
  285. r[5] ^= load64(scratchpad + spAddr0 + 40);
  286. r[6] ^= load64(scratchpad + spAddr0 + 48);
  287. r[7] ^= load64(scratchpad + spAddr0 + 56);
  288. f[0] = load_cvt_i32x2(scratchpad + spAddr1 + 0);
  289. f[1] = load_cvt_i32x2(scratchpad + spAddr1 + 8);
  290. f[2] = load_cvt_i32x2(scratchpad + spAddr1 + 16);
  291. f[3] = load_cvt_i32x2(scratchpad + spAddr1 + 24);
  292. e[0] = ieee_set_exponent<-240>(load_cvt_i32x2(scratchpad + spAddr1 + 32));
  293. e[1] = ieee_set_exponent<-240>(load_cvt_i32x2(scratchpad + spAddr1 + 40));
  294. e[2] = ieee_set_exponent<-240>(load_cvt_i32x2(scratchpad + spAddr1 + 48));
  295. e[3] = ieee_set_exponent<-240>(load_cvt_i32x2(scratchpad + spAddr1 + 56));
  296. if (trace) {
  297. std::cout << "iteration " << std::dec << ic << std::endl;
  298. std::cout << "spAddr " << std::hex << std::setw(8) << std::setfill('0') << spAddr1 << " / " << std::setw(8) << std::setfill('0') << spAddr0 << std::endl;
  299. std::cout << "ma/mx " << std::hex << std::setw(8) << std::setfill('0') << mem.ma << std::setw(8) << std::setfill('0') << mem.mx << std::endl;
  300. printState(r, f, e, a);
  301. std::cout << "-----------------------------------" << std::endl;
  302. }
  303. executeBytecode(r, f, e, a);
  304. mem.mx ^= r[readReg2] ^ r[readReg3];
  305. mem.mx &= CacheLineAlignMask;
  306. if (superscalar) {
  307. executeSuperscalar(datasetBase + mem.ma / CacheLineSize, r);
  308. }
  309. else {
  310. Cache& cache = mem.ds.cache;
  311. uint64_t datasetLine[CacheLineSize / sizeof(uint64_t)];
  312. initBlock(cache, (uint8_t*)datasetLine, datasetBase + mem.ma / CacheLineSize, RANDOMX_CACHE_ACCESSES / 8);
  313. for (int i = 0; i < RegistersCount; ++i)
  314. r[i] ^= datasetLine[i];
  315. }
  316. std::swap(mem.mx, mem.ma);
  317. if (trace) {
  318. std::cout << "iteration " << std::dec << ic << std::endl;
  319. std::cout << "spAddr " << std::hex << std::setw(8) << std::setfill('0') << spAddr1 << " / " << std::setw(8) << std::setfill('0') << spAddr0 << std::endl;
  320. std::cout << "ma/mx " << std::hex << std::setw(8) << std::setfill('0') << mem.ma << std::setw(8) << std::setfill('0') << mem.mx << std::endl;
  321. printState(r, f, e, a);
  322. std::cout << "===================================" << std::endl;
  323. }
  324. store64(scratchpad + spAddr1 + 0, r[0]);
  325. store64(scratchpad + spAddr1 + 8, r[1]);
  326. store64(scratchpad + spAddr1 + 16, r[2]);
  327. store64(scratchpad + spAddr1 + 24, r[3]);
  328. store64(scratchpad + spAddr1 + 32, r[4]);
  329. store64(scratchpad + spAddr1 + 40, r[5]);
  330. store64(scratchpad + spAddr1 + 48, r[6]);
  331. store64(scratchpad + spAddr1 + 56, r[7]);
  332. f[0] = _mm_xor_pd(f[0], e[0]);
  333. f[1] = _mm_xor_pd(f[1], e[1]);
  334. f[2] = _mm_xor_pd(f[2], e[2]);
  335. f[3] = _mm_xor_pd(f[3], e[3]);
  336. #ifdef FPUCHECK
  337. for(int i = 0; i < 4; ++i) {
  338. double lo = *(((double*)&f[i]) + 0);
  339. double hi = *(((double*)&f[i]) + 1);
  340. if (isDenormal(lo) || isDenormal(hi)) {
  341. std::stringstream ss;
  342. ss << "Denormal f" << i;
  343. throw std::runtime_error(ss.str());
  344. }
  345. }
  346. #endif
  347. _mm_store_pd((double*)(scratchpad + spAddr0 + 0), f[0]);
  348. _mm_store_pd((double*)(scratchpad + spAddr0 + 16), f[1]);
  349. _mm_store_pd((double*)(scratchpad + spAddr0 + 32), f[2]);
  350. _mm_store_pd((double*)(scratchpad + spAddr0 + 48), f[3]);
  351. spAddr0 = 0;
  352. spAddr1 = 0;
  353. }
  354. store64(&reg.r[0], r[0]);
  355. store64(&reg.r[1], r[1]);
  356. store64(&reg.r[2], r[2]);
  357. store64(&reg.r[3], r[3]);
  358. store64(&reg.r[4], r[4]);
  359. store64(&reg.r[5], r[5]);
  360. store64(&reg.r[6], r[6]);
  361. store64(&reg.r[7], r[7]);
  362. _mm_store_pd(&reg.f[0].lo, f[0]);
  363. _mm_store_pd(&reg.f[1].lo, f[1]);
  364. _mm_store_pd(&reg.f[2].lo, f[2]);
  365. _mm_store_pd(&reg.f[3].lo, f[3]);
  366. _mm_store_pd(&reg.e[0].lo, e[0]);
  367. _mm_store_pd(&reg.e[1].lo, e[1]);
  368. _mm_store_pd(&reg.e[2].lo, e[2]);
  369. _mm_store_pd(&reg.e[3].lo, e[3]);
  370. }
  371. template void InterpretedVirtualMachine<true>::execute();
  372. template void InterpretedVirtualMachine<false>::execute();
  373. static int getConditionRegister(int(&registerUsage)[8]) {
  374. int min = INT_MAX;
  375. int minIndex;
  376. for (unsigned i = 0; i < 8; ++i) {
  377. if (registerUsage[i] < min) {
  378. min = registerUsage[i];
  379. minIndex = i;
  380. }
  381. }
  382. return minIndex;
  383. }
  384. constexpr uint64_t superscalarMul0 = 6364136223846793005ULL;
  385. constexpr uint64_t superscalarAdd1 = 9298410992540426748ULL;
  386. constexpr uint64_t superscalarAdd2 = 12065312585734608966ULL;
  387. constexpr uint64_t superscalarAdd3 = 9306329213124610396ULL;
  388. constexpr uint64_t superscalarAdd4 = 5281919268842080866ULL;
  389. constexpr uint64_t superscalarAdd5 = 10536153434571861004ULL;
  390. constexpr uint64_t superscalarAdd6 = 3398623926847679864ULL;
  391. constexpr uint64_t superscalarAdd7 = 9549104520008361294ULL;
  392. static uint8_t* getMixBlock(uint64_t registerValue, Cache& cache) {
  393. uint8_t* mixBlock;
  394. if (RANDOMX_ARGON_GROWTH == 0) {
  395. constexpr uint32_t mask = (RANDOMX_ARGON_MEMORY * ArgonBlockSize / CacheLineSize - 1);
  396. mixBlock = cache.memory + (registerValue & mask) * CacheLineSize;
  397. }
  398. else {
  399. const uint32_t modulus = cache.size / CacheLineSize;
  400. mixBlock = cache.memory + (registerValue % modulus) * CacheLineSize;
  401. }
  402. return mixBlock;
  403. }
  404. template<bool superscalar>
  405. void InterpretedVirtualMachine<superscalar>::executeSuperscalar(int_reg_t(&r)[8], LightProgram& prog, std::vector<uint64_t>& reciprocals) {
  406. for (unsigned j = 0; j < prog.getSize(); ++j) {
  407. Instruction& instr = prog(j);
  408. switch (instr.opcode)
  409. {
  410. case RandomX::LightInstructionType::ISUB_R:
  411. r[instr.dst] -= r[instr.src];
  412. break;
  413. case RandomX::LightInstructionType::IXOR_R:
  414. r[instr.dst] ^= r[instr.src];
  415. break;
  416. case RandomX::LightInstructionType::IADD_RS:
  417. r[instr.dst] += r[instr.src] << (instr.mod % 4);
  418. break;
  419. case RandomX::LightInstructionType::IMUL_R:
  420. r[instr.dst] *= r[instr.src];
  421. break;
  422. case RandomX::LightInstructionType::IROR_C:
  423. r[instr.dst] = rotr(r[instr.dst], instr.getImm32());
  424. break;
  425. case RandomX::LightInstructionType::IADD_C7:
  426. case RandomX::LightInstructionType::IADD_C8:
  427. case RandomX::LightInstructionType::IADD_C9:
  428. r[instr.dst] += signExtend2sCompl(instr.getImm32());
  429. break;
  430. case RandomX::LightInstructionType::IXOR_C7:
  431. case RandomX::LightInstructionType::IXOR_C8:
  432. case RandomX::LightInstructionType::IXOR_C9:
  433. r[instr.dst] ^= signExtend2sCompl(instr.getImm32());
  434. break;
  435. case RandomX::LightInstructionType::IMULH_R:
  436. r[instr.dst] = mulh(r[instr.dst], r[instr.src]);
  437. break;
  438. case RandomX::LightInstructionType::ISMULH_R:
  439. r[instr.dst] = smulh(r[instr.dst], r[instr.src]);
  440. break;
  441. case RandomX::LightInstructionType::IMUL_RCP:
  442. if(superscalar)
  443. r[instr.dst] *= reciprocals[instr.getImm32()];
  444. else
  445. r[instr.dst] *= reciprocal(instr.getImm32());
  446. break;
  447. default:
  448. UNREACHABLE;
  449. }
  450. }
  451. }
  452. template<bool superscalar>
  453. void InterpretedVirtualMachine<superscalar>::executeSuperscalar(uint32_t blockNumber, int_reg_t(&r)[8]) {
  454. int_reg_t rl[8];
  455. uint8_t* mixBlock;
  456. uint64_t registerValue = blockNumber;
  457. rl[0] = (blockNumber + 1) * superscalarMul0;
  458. rl[1] = rl[0] ^ superscalarAdd1;
  459. rl[2] = rl[0] ^ superscalarAdd2;
  460. rl[3] = rl[0] ^ superscalarAdd3;
  461. rl[4] = rl[0] ^ superscalarAdd4;
  462. rl[5] = rl[0] ^ superscalarAdd5;
  463. rl[6] = rl[0] ^ superscalarAdd6;
  464. rl[7] = rl[0] ^ superscalarAdd7;
  465. Cache& cache = mem.ds.cache;
  466. for (unsigned i = 0; i < RANDOMX_CACHE_ACCESSES; ++i) {
  467. mixBlock = getMixBlock(registerValue, cache);
  468. LightProgram& prog = superScalarPrograms[i];
  469. executeSuperscalar(rl, prog, reciprocals);
  470. for(unsigned q = 0; q < 8; ++q)
  471. rl[q] ^= load64(mixBlock + 8 * q);
  472. registerValue = rl[prog.getAddressRegister()];
  473. }
  474. for (unsigned q = 0; q < 8; ++q)
  475. r[q] ^= rl[q];
  476. }
  477. template<bool superscalar>
  478. void InterpretedVirtualMachine<superscalar>::precompileSuperscalar(LightProgram* programs) {
  479. memcpy(superScalarPrograms, programs, sizeof(superScalarPrograms));
  480. reciprocals.clear();
  481. for (unsigned i = 0; i < RANDOMX_CACHE_ACCESSES; ++i) {
  482. for (unsigned j = 0; j < superScalarPrograms[i].getSize(); ++j) {
  483. Instruction& instr = superScalarPrograms[i](j);
  484. if (instr.opcode == LightInstructionType::IMUL_RCP) {
  485. auto rcp = reciprocal(instr.getImm32());
  486. instr.setImm32(reciprocals.size());
  487. reciprocals.push_back(rcp);
  488. }
  489. }
  490. }
  491. }
  492. #include "instructionWeights.hpp"
  493. template<bool superscalar>
  494. void InterpretedVirtualMachine<superscalar>::precompileProgram(int_reg_t(&r)[8], __m128d (&f)[4], __m128d (&e)[4], __m128d (&a)[4]) {
  495. int registerUsage[8];
  496. for (unsigned i = 0; i < 8; ++i) {
  497. registerUsage[i] = -1;
  498. }
  499. for (unsigned i = 0; i < RANDOMX_PROGRAM_SIZE; ++i) {
  500. auto& instr = program(i);
  501. auto& ibc = byteCode[i];
  502. switch (instr.opcode) {
  503. CASE_REP(IADD_RS) {
  504. auto dst = instr.dst % RegistersCount;
  505. auto src = instr.src % RegistersCount;
  506. ibc.type = InstructionType::IADD_RS;
  507. ibc.idst = &r[dst];
  508. if (dst != 5) {
  509. ibc.isrc = &r[src];
  510. ibc.shift = instr.mod % 4;
  511. ibc.imm = 0;
  512. }
  513. else {
  514. ibc.isrc = &r[src];
  515. ibc.shift = instr.mod % 4;
  516. ibc.imm = signExtend2sCompl(instr.getImm32());
  517. }
  518. registerUsage[instr.dst] = i;
  519. } break;
  520. CASE_REP(IADD_M) {
  521. auto dst = instr.dst % RegistersCount;
  522. auto src = instr.src % RegistersCount;
  523. ibc.type = InstructionType::IADD_M;
  524. ibc.idst = &r[dst];
  525. if (instr.src != instr.dst) {
  526. ibc.isrc = &r[src];
  527. ibc.memMask = ((instr.mod % 4) ? ScratchpadL1Mask : ScratchpadL2Mask);
  528. }
  529. else {
  530. ibc.imm = instr.getImm32();
  531. ibc.isrc = &ibc.imm;
  532. ibc.memMask = ScratchpadL3Mask;
  533. }
  534. registerUsage[instr.dst] = i;
  535. } break;
  536. CASE_REP(IADD_RC) {
  537. auto dst = instr.dst % RegistersCount;
  538. auto src = instr.src % RegistersCount;
  539. ibc.type = InstructionType::IADD_RC;
  540. ibc.idst = &r[dst];
  541. ibc.isrc = &r[src];
  542. ibc.imm = signExtend2sCompl(instr.getImm32());
  543. registerUsage[instr.dst] = i;
  544. } break;
  545. CASE_REP(ISUB_R) {
  546. auto dst = instr.dst % RegistersCount;
  547. auto src = instr.src % RegistersCount;
  548. ibc.type = InstructionType::ISUB_R;
  549. ibc.idst = &r[dst];
  550. if (src != dst) {
  551. ibc.isrc = &r[src];
  552. }
  553. else {
  554. ibc.imm = signExtend2sCompl(instr.getImm32());
  555. ibc.isrc = &ibc.imm;
  556. }
  557. registerUsage[instr.dst] = i;
  558. } break;
  559. CASE_REP(ISUB_M) {
  560. auto dst = instr.dst % RegistersCount;
  561. auto src = instr.src % RegistersCount;
  562. ibc.type = InstructionType::ISUB_M;
  563. ibc.idst = &r[dst];
  564. if (instr.src != instr.dst) {
  565. ibc.isrc = &r[src];
  566. ibc.memMask = ((instr.mod % 4) ? ScratchpadL1Mask : ScratchpadL2Mask);
  567. }
  568. else {
  569. ibc.imm = instr.getImm32();
  570. ibc.isrc = &ibc.imm;
  571. ibc.memMask = ScratchpadL3Mask;
  572. }
  573. registerUsage[instr.dst] = i;
  574. } break;
  575. CASE_REP(IMUL_9C) {
  576. auto dst = instr.dst % RegistersCount;
  577. ibc.type = InstructionType::IMUL_9C;
  578. ibc.idst = &r[dst];
  579. ibc.imm = signExtend2sCompl(instr.getImm32());
  580. registerUsage[instr.dst] = i;
  581. } break;
  582. CASE_REP(IMUL_R) {
  583. auto dst = instr.dst % RegistersCount;
  584. auto src = instr.src % RegistersCount;
  585. ibc.type = InstructionType::IMUL_R;
  586. ibc.idst = &r[dst];
  587. if (src != dst) {
  588. ibc.isrc = &r[src];
  589. }
  590. else {
  591. ibc.imm = signExtend2sCompl(instr.getImm32());
  592. ibc.isrc = &ibc.imm;
  593. }
  594. registerUsage[instr.dst] = i;
  595. } break;
  596. CASE_REP(IMUL_M) {
  597. auto dst = instr.dst % RegistersCount;
  598. auto src = instr.src % RegistersCount;
  599. ibc.type = InstructionType::IMUL_M;
  600. ibc.idst = &r[dst];
  601. if (instr.src != instr.dst) {
  602. ibc.isrc = &r[src];
  603. ibc.memMask = ((instr.mod % 4) ? ScratchpadL1Mask : ScratchpadL2Mask);
  604. }
  605. else {
  606. ibc.imm = instr.getImm32();
  607. ibc.isrc = &ibc.imm;
  608. ibc.memMask = ScratchpadL3Mask;
  609. }
  610. registerUsage[instr.dst] = i;
  611. } break;
  612. CASE_REP(IMULH_R) {
  613. auto dst = instr.dst % RegistersCount;
  614. auto src = instr.src % RegistersCount;
  615. ibc.type = InstructionType::IMULH_R;
  616. ibc.idst = &r[dst];
  617. ibc.isrc = &r[src];
  618. registerUsage[instr.dst] = i;
  619. } break;
  620. CASE_REP(IMULH_M) {
  621. auto dst = instr.dst % RegistersCount;
  622. auto src = instr.src % RegistersCount;
  623. ibc.type = InstructionType::IMULH_M;
  624. ibc.idst = &r[dst];
  625. if (instr.src != instr.dst) {
  626. ibc.isrc = &r[src];
  627. ibc.memMask = ((instr.mod % 4) ? ScratchpadL1Mask : ScratchpadL2Mask);
  628. }
  629. else {
  630. ibc.imm = instr.getImm32();
  631. ibc.isrc = &ibc.imm;
  632. ibc.memMask = ScratchpadL3Mask;
  633. }
  634. registerUsage[instr.dst] = i;
  635. } break;
  636. CASE_REP(ISMULH_R) {
  637. auto dst = instr.dst % RegistersCount;
  638. auto src = instr.src % RegistersCount;
  639. ibc.type = InstructionType::ISMULH_R;
  640. ibc.idst = &r[dst];
  641. ibc.isrc = &r[src];
  642. registerUsage[instr.dst] = i;
  643. } break;
  644. CASE_REP(ISMULH_M) {
  645. auto dst = instr.dst % RegistersCount;
  646. auto src = instr.src % RegistersCount;
  647. ibc.type = InstructionType::ISMULH_M;
  648. ibc.idst = &r[dst];
  649. if (instr.src != instr.dst) {
  650. ibc.isrc = &r[src];
  651. ibc.memMask = ((instr.mod % 4) ? ScratchpadL1Mask : ScratchpadL2Mask);
  652. }
  653. else {
  654. ibc.imm = instr.getImm32();
  655. ibc.isrc = &ibc.imm;
  656. ibc.memMask = ScratchpadL3Mask;
  657. }
  658. registerUsage[instr.dst] = i;
  659. } break;
  660. CASE_REP(IMUL_RCP) {
  661. uint32_t divisor = instr.getImm32();
  662. if (divisor != 0) {
  663. auto dst = instr.dst % RegistersCount;
  664. ibc.type = InstructionType::IMUL_R;
  665. ibc.idst = &r[dst];
  666. ibc.imm = reciprocal(divisor);
  667. ibc.isrc = &ibc.imm;
  668. registerUsage[instr.dst] = i;
  669. }
  670. else {
  671. ibc.type = InstructionType::NOP;
  672. }
  673. } break;
  674. CASE_REP(INEG_R) {
  675. auto dst = instr.dst % RegistersCount;
  676. ibc.type = InstructionType::INEG_R;
  677. ibc.idst = &r[dst];
  678. registerUsage[instr.dst] = i;
  679. } break;
  680. CASE_REP(IXOR_R) {
  681. auto dst = instr.dst % RegistersCount;
  682. auto src = instr.src % RegistersCount;
  683. ibc.type = InstructionType::IXOR_R;
  684. ibc.idst = &r[dst];
  685. if (src != dst) {
  686. ibc.isrc = &r[src];
  687. }
  688. else {
  689. ibc.imm = signExtend2sCompl(instr.getImm32());
  690. ibc.isrc = &ibc.imm;
  691. }
  692. registerUsage[instr.dst] = i;
  693. } break;
  694. CASE_REP(IXOR_M) {
  695. auto dst = instr.dst % RegistersCount;
  696. auto src = instr.src % RegistersCount;
  697. ibc.type = InstructionType::IXOR_M;
  698. ibc.idst = &r[dst];
  699. if (instr.src != instr.dst) {
  700. ibc.isrc = &r[src];
  701. ibc.memMask = ((instr.mod % 4) ? ScratchpadL1Mask : ScratchpadL2Mask);
  702. }
  703. else {
  704. ibc.imm = instr.getImm32();
  705. ibc.isrc = &ibc.imm;
  706. ibc.memMask = ScratchpadL3Mask;
  707. }
  708. registerUsage[instr.dst] = i;
  709. } break;
  710. CASE_REP(IROR_R) {
  711. auto dst = instr.dst % RegistersCount;
  712. auto src = instr.src % RegistersCount;
  713. ibc.type = InstructionType::IROR_R;
  714. ibc.idst = &r[dst];
  715. if (src != dst) {
  716. ibc.isrc = &r[src];
  717. }
  718. else {
  719. ibc.imm = instr.getImm32();
  720. ibc.isrc = &ibc.imm;
  721. }
  722. registerUsage[instr.dst] = i;
  723. } break;
  724. CASE_REP(IROL_R) {
  725. auto dst = instr.dst % RegistersCount;
  726. auto src = instr.src % RegistersCount;
  727. ibc.type = InstructionType::IROL_R;
  728. ibc.idst = &r[dst];
  729. if (src != dst) {
  730. ibc.isrc = &r[src];
  731. }
  732. else {
  733. ibc.imm = instr.getImm32();
  734. ibc.isrc = &ibc.imm;
  735. }
  736. registerUsage[instr.dst] = i;
  737. } break;
  738. CASE_REP(ISWAP_R) {
  739. auto dst = instr.dst % RegistersCount;
  740. auto src = instr.src % RegistersCount;
  741. if (src != dst) {
  742. ibc.idst = &r[dst];
  743. ibc.isrc = &r[src];
  744. ibc.type = InstructionType::ISWAP_R;
  745. registerUsage[instr.dst] = i;
  746. registerUsage[instr.src] = i;
  747. }
  748. else {
  749. ibc.type = InstructionType::NOP;
  750. }
  751. } break;
  752. CASE_REP(FSWAP_R) {
  753. auto dst = instr.dst % RegistersCount;
  754. ibc.type = InstructionType::FSWAP_R;
  755. if (dst < 4)
  756. ibc.fdst = &f[dst];
  757. else
  758. ibc.fdst = &e[dst - 4];
  759. } break;
  760. CASE_REP(FADD_R) {
  761. auto dst = instr.dst % 4;
  762. auto src = instr.src % 4;
  763. ibc.type = InstructionType::FADD_R;
  764. ibc.fdst = &f[dst];
  765. ibc.fsrc = &a[src];
  766. } break;
  767. CASE_REP(FADD_M) {
  768. auto dst = instr.dst % 4;
  769. auto src = instr.src % 8;
  770. ibc.type = InstructionType::FADD_M;
  771. ibc.fdst = &f[dst];
  772. ibc.isrc = &r[src];
  773. ibc.memMask = ((instr.mod % 4) ? ScratchpadL1Mask : ScratchpadL2Mask);
  774. } break;
  775. CASE_REP(FSUB_R) {
  776. auto dst = instr.dst % 4;
  777. auto src = instr.src % 4;
  778. ibc.type = InstructionType::FSUB_R;
  779. ibc.fdst = &f[dst];
  780. ibc.fsrc = &a[src];
  781. } break;
  782. CASE_REP(FSUB_M) {
  783. auto dst = instr.dst % 4;
  784. auto src = instr.src % 8;
  785. ibc.type = InstructionType::FSUB_M;
  786. ibc.fdst = &f[dst];
  787. ibc.isrc = &r[src];
  788. ibc.memMask = ((instr.mod % 4) ? ScratchpadL1Mask : ScratchpadL2Mask);
  789. } break;
  790. CASE_REP(FSCAL_R) {
  791. auto dst = instr.dst % 4;
  792. ibc.fdst = &f[dst];
  793. ibc.type = InstructionType::FSCAL_R;
  794. } break;
  795. CASE_REP(FMUL_R) {
  796. auto dst = instr.dst % 4;
  797. auto src = instr.src % 4;
  798. ibc.type = InstructionType::FMUL_R;
  799. ibc.fdst = &e[dst];
  800. ibc.fsrc = &a[src];
  801. } break;
  802. CASE_REP(FDIV_M) {
  803. auto dst = instr.dst % 4;
  804. auto src = instr.src % 8;
  805. ibc.type = InstructionType::FDIV_M;
  806. ibc.fdst = &e[dst];
  807. ibc.isrc = &r[src];
  808. ibc.memMask = ((instr.mod % 4) ? ScratchpadL1Mask : ScratchpadL2Mask);
  809. } break;
  810. CASE_REP(FSQRT_R) {
  811. auto dst = instr.dst % 4;
  812. ibc.type = InstructionType::FSQRT_R;
  813. ibc.fdst = &e[dst];
  814. } break;
  815. CASE_REP(COND_R) {
  816. auto dst = instr.dst % RegistersCount;
  817. auto src = instr.src % RegistersCount;
  818. ibc.type = InstructionType::COND_R;
  819. ibc.idst = &r[dst];
  820. ibc.isrc = &r[src];
  821. ibc.condition = (instr.mod >> 2) & 7;
  822. ibc.imm = instr.getImm32();
  823. //jump condition
  824. int reg = getConditionRegister(registerUsage);
  825. ibc.target = registerUsage[reg];
  826. ibc.shift = (instr.mod >> 5);
  827. ibc.creg = &r[reg];
  828. for (unsigned j = 0; j < 8; ++j) { //mark all registers as used
  829. registerUsage[j] = i;
  830. }
  831. } break;
  832. CASE_REP(COND_M) {
  833. auto dst = instr.dst % RegistersCount;
  834. auto src = instr.src % RegistersCount;
  835. ibc.type = InstructionType::COND_M;
  836. ibc.idst = &r[dst];
  837. ibc.isrc = &r[src];
  838. ibc.condition = (instr.mod >> 2) & 7;
  839. ibc.imm = instr.getImm32();
  840. ibc.memMask = ((instr.mod % 4) ? ScratchpadL1Mask : ScratchpadL2Mask);
  841. //jump condition
  842. int reg = getConditionRegister(registerUsage);
  843. ibc.target = registerUsage[reg];
  844. ibc.shift = (instr.mod >> 5);
  845. ibc.creg = &r[reg];
  846. for (unsigned j = 0; j < 8; ++j) { //mark all registers as used
  847. registerUsage[j] = i;
  848. }
  849. } break;
  850. CASE_REP(CFROUND) {
  851. auto src = instr.src % 8;
  852. ibc.isrc = &r[src];
  853. ibc.type = InstructionType::CFROUND;
  854. ibc.imm = instr.getImm32() & 63;
  855. } break;
  856. CASE_REP(ISTORE) {
  857. auto dst = instr.dst % RegistersCount;
  858. auto src = instr.src % RegistersCount;
  859. ibc.type = InstructionType::ISTORE;
  860. ibc.idst = &r[dst];
  861. ibc.isrc = &r[src];
  862. ibc.memMask = ((instr.mod % 4) ? ScratchpadL1Mask : ScratchpadL2Mask);
  863. } break;
  864. CASE_REP(NOP) {
  865. ibc.type = InstructionType::NOP;
  866. } break;
  867. default:
  868. UNREACHABLE;
  869. }
  870. }
  871. }
  872. }