assembly_generator_x86.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736
  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. #include <climits>
  17. #include "assembly_generator_x86.hpp"
  18. #include "common.hpp"
  19. #include "reciprocal.h"
  20. #include "program.hpp"
  21. #include "superscalar.hpp"
  22. namespace randomx {
  23. static const char* regR[8] = { "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" };
  24. static const char* regR32[8] = { "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" };
  25. static const char* regFE[8] = { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7" };
  26. static const char* regF[4] = { "xmm0", "xmm1", "xmm2", "xmm3" };
  27. static const char* regE[4] = { "xmm4", "xmm5", "xmm6", "xmm7" };
  28. static const char* regA[4] = { "xmm8", "xmm9", "xmm10", "xmm11" };
  29. static const char* regA4 = "xmm12";
  30. static const char* dblMin = "xmm13";
  31. static const char* absMask = "xmm14";
  32. static const char* signMask = "xmm15";
  33. static const char* regMx = "rbp";
  34. static const char* regIc = "rbx";
  35. static const char* regIc32 = "ebx";
  36. static const char* regIc8 = "bl";
  37. static const char* regDatasetAddr = "rdi";
  38. static const char* regScratchpadAddr = "rsi";
  39. void AssemblyGeneratorX86::generateProgram(Program& prog) {
  40. for (unsigned i = 0; i < 8; ++i) {
  41. registerUsage[i] = -1;
  42. }
  43. asmCode.str(std::string()); //clear
  44. for (unsigned i = 0; i < prog.getSize(); ++i) {
  45. asmCode << "randomx_isn_" << i << ":" << std::endl;
  46. Instruction& instr = prog(i);
  47. instr.src %= RegistersCount;
  48. instr.dst %= RegistersCount;
  49. generateCode(instr, i);
  50. }
  51. }
  52. void AssemblyGeneratorX86::generateAsm(SuperscalarProgram& prog) {
  53. asmCode.str(std::string()); //clear
  54. asmCode << "ALIGN 16" << std::endl;
  55. for (unsigned i = 0; i < prog.getSize(); ++i) {
  56. Instruction& instr = prog(i);
  57. switch (instr.opcode)
  58. {
  59. case SuperscalarInstructionType::ISUB_R:
  60. asmCode << "sub " << regR[instr.dst] << ", " << regR[instr.src] << std::endl;
  61. break;
  62. case SuperscalarInstructionType::IXOR_R:
  63. asmCode << "xor " << regR[instr.dst] << ", " << regR[instr.src] << std::endl;
  64. break;
  65. case SuperscalarInstructionType::IADD_RS:
  66. asmCode << "lea " << regR[instr.dst] << ", [" << regR[instr.dst] << "+" << regR[instr.src] << "*" << (1 << (instr.getModShift2())) << "]" << std::endl;
  67. break;
  68. case SuperscalarInstructionType::IMUL_R:
  69. asmCode << "imul " << regR[instr.dst] << ", " << regR[instr.src] << std::endl;
  70. break;
  71. case SuperscalarInstructionType::IROR_C:
  72. asmCode << "ror " << regR[instr.dst] << ", " << instr.getImm32() << std::endl;
  73. break;
  74. case SuperscalarInstructionType::IADD_C7:
  75. asmCode << "add " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl;
  76. break;
  77. case SuperscalarInstructionType::IXOR_C7:
  78. asmCode << "xor " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl;
  79. break;
  80. case SuperscalarInstructionType::IADD_C8:
  81. asmCode << "add " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl;
  82. asmCode << "nop" << std::endl;
  83. break;
  84. case SuperscalarInstructionType::IXOR_C8:
  85. asmCode << "xor " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl;
  86. asmCode << "nop" << std::endl;
  87. break;
  88. case SuperscalarInstructionType::IADD_C9:
  89. asmCode << "add " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl;
  90. asmCode << "xchg ax, ax ;nop" << std::endl;
  91. break;
  92. case SuperscalarInstructionType::IXOR_C9:
  93. asmCode << "xor " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl;
  94. asmCode << "xchg ax, ax ;nop" << std::endl;
  95. break;
  96. case SuperscalarInstructionType::IMULH_R:
  97. asmCode << "mov rax, " << regR[instr.dst] << std::endl;
  98. asmCode << "mul " << regR[instr.src] << std::endl;
  99. asmCode << "mov " << regR[instr.dst] << ", rdx" << std::endl;
  100. break;
  101. case SuperscalarInstructionType::ISMULH_R:
  102. asmCode << "mov rax, " << regR[instr.dst] << std::endl;
  103. asmCode << "imul " << regR[instr.src] << std::endl;
  104. asmCode << "mov " << regR[instr.dst] << ", rdx" << std::endl;
  105. break;
  106. case SuperscalarInstructionType::IMUL_RCP:
  107. asmCode << "mov rax, " << (int64_t)randomx_reciprocal(instr.getImm32()) << std::endl;
  108. asmCode << "imul " << regR[instr.dst] << ", rax" << std::endl;
  109. break;
  110. default:
  111. UNREACHABLE;
  112. }
  113. }
  114. }
  115. void AssemblyGeneratorX86::generateC(SuperscalarProgram& prog) {
  116. asmCode.str(std::string()); //clear
  117. asmCode << "#include <stdint.h>" << std::endl;
  118. asmCode << "#if defined(__SIZEOF_INT128__)" << std::endl;
  119. asmCode << " static inline uint64_t mulh(uint64_t a, uint64_t b) {" << std::endl;
  120. asmCode << " return ((unsigned __int128)a * b) >> 64;" << std::endl;
  121. asmCode << " }" << std::endl;
  122. asmCode << " static inline int64_t smulh(int64_t a, int64_t b) {" << std::endl;
  123. asmCode << " return ((__int128)a * b) >> 64;" << std::endl;
  124. asmCode << " }" << std::endl;
  125. asmCode << " #define HAVE_MULH" << std::endl;
  126. asmCode << " #define HAVE_SMULH" << std::endl;
  127. asmCode << "#endif" << std::endl;
  128. asmCode << "#if defined(_MSC_VER)" << std::endl;
  129. asmCode << " #define HAS_VALUE(X) X ## 0" << std::endl;
  130. asmCode << " #define EVAL_DEFINE(X) HAS_VALUE(X)" << std::endl;
  131. asmCode << " #include <intrin.h>" << std::endl;
  132. asmCode << " #include <stdlib.h>" << std::endl;
  133. asmCode << " static __inline uint64_t rotr(uint64_t x , int c) {" << std::endl;
  134. asmCode << " return _rotr64(x, c);" << std::endl;
  135. asmCode << " }" << std::endl;
  136. asmCode << " #define HAVE_ROTR" << std::endl;
  137. asmCode << " #if EVAL_DEFINE(__MACHINEARM64_X64(1))" << std::endl;
  138. asmCode << " static __inline uint64_t mulh(uint64_t a, uint64_t b) {" << std::endl;
  139. asmCode << " return __umulh(a, b);" << std::endl;
  140. asmCode << " }" << std::endl;
  141. asmCode << " #define HAVE_MULH" << std::endl;
  142. asmCode << " #endif" << std::endl;
  143. asmCode << " #if EVAL_DEFINE(__MACHINEX64(1))" << std::endl;
  144. asmCode << " static __inline int64_t smulh(int64_t a, int64_t b) {" << std::endl;
  145. asmCode << " int64_t hi;" << std::endl;
  146. asmCode << " _mul128(a, b, &hi);" << std::endl;
  147. asmCode << " return hi;" << std::endl;
  148. asmCode << " }" << std::endl;
  149. asmCode << " #define HAVE_SMULH" << std::endl;
  150. asmCode << " #endif" << std::endl;
  151. asmCode << "#endif" << std::endl;
  152. asmCode << "#ifndef HAVE_ROTR" << std::endl;
  153. asmCode << " static inline uint64_t rotr(uint64_t a, int b) {" << std::endl;
  154. asmCode << " return (a >> b) | (a << (64 - b));" << std::endl;
  155. asmCode << " }" << std::endl;
  156. asmCode << " #define HAVE_ROTR" << std::endl;
  157. asmCode << "#endif" << std::endl;
  158. asmCode << "#if !defined(HAVE_MULH) || !defined(HAVE_SMULH) || !defined(HAVE_ROTR)" << std::endl;
  159. asmCode << " #error \"Required functions are not defined\"" << std::endl;
  160. asmCode << "#endif" << std::endl;
  161. asmCode << "void superScalar(uint64_t r[8]) {" << std::endl;
  162. asmCode << "uint64_t r8 = r[0], r9 = r[1], r10 = r[2], r11 = r[3], r12 = r[4], r13 = r[5], r14 = r[6], r15 = r[7];" << std::endl;
  163. for (unsigned i = 0; i < prog.getSize(); ++i) {
  164. Instruction& instr = prog(i);
  165. switch (instr.opcode)
  166. {
  167. case SuperscalarInstructionType::ISUB_R:
  168. asmCode << regR[instr.dst] << " -= " << regR[instr.src] << ";" << std::endl;
  169. break;
  170. case SuperscalarInstructionType::IXOR_R:
  171. asmCode << regR[instr.dst] << " ^= " << regR[instr.src] << ";" << std::endl;
  172. break;
  173. case SuperscalarInstructionType::IADD_RS:
  174. asmCode << regR[instr.dst] << " += " << regR[instr.src] << "*" << (1 << (instr.getModShift2())) << ";" << std::endl;
  175. break;
  176. case SuperscalarInstructionType::IMUL_R:
  177. asmCode << regR[instr.dst] << " *= " << regR[instr.src] << ";" << std::endl;
  178. break;
  179. case SuperscalarInstructionType::IROR_C:
  180. asmCode << regR[instr.dst] << " = rotr(" << regR[instr.dst] << ", " << instr.getImm32() << ");" << std::endl;
  181. break;
  182. case SuperscalarInstructionType::IADD_C7:
  183. case SuperscalarInstructionType::IADD_C8:
  184. case SuperscalarInstructionType::IADD_C9:
  185. asmCode << regR[instr.dst] << " += " << (int32_t)instr.getImm32() << ";" << std::endl;
  186. break;
  187. case SuperscalarInstructionType::IXOR_C7:
  188. case SuperscalarInstructionType::IXOR_C8:
  189. case SuperscalarInstructionType::IXOR_C9:
  190. asmCode << regR[instr.dst] << " ^= " << (int32_t)instr.getImm32() << ";" << std::endl;
  191. break;
  192. case SuperscalarInstructionType::IMULH_R:
  193. asmCode << regR[instr.dst] << " = mulh(" << regR[instr.dst] << ", " << regR[instr.src] << ");" << std::endl;
  194. break;
  195. case SuperscalarInstructionType::ISMULH_R:
  196. asmCode << regR[instr.dst] << " = smulh(" << regR[instr.dst] << ", " << regR[instr.src] << ");" << std::endl;
  197. break;
  198. case SuperscalarInstructionType::IMUL_RCP:
  199. asmCode << regR[instr.dst] << " *= " << (int64_t)randomx_reciprocal(instr.getImm32()) << ";" << std::endl;
  200. break;
  201. default:
  202. UNREACHABLE;
  203. }
  204. }
  205. asmCode << "r[0] = r8; r[1] = r9; r[2] = r10; r[3] = r11; r[4] = r12; r[5] = r13; r[6] = r14; r[7] = r15;" << std::endl;
  206. asmCode << "}" << std::endl;
  207. }
  208. int AssemblyGeneratorX86::getConditionRegister() {
  209. int min = INT_MAX;
  210. int minIndex;
  211. for (unsigned i = 0; i < 8; ++i) {
  212. if (registerUsage[i] < min) {
  213. min = registerUsage[i];
  214. minIndex = i;
  215. }
  216. }
  217. return minIndex;
  218. }
  219. void AssemblyGeneratorX86::traceint(Instruction& instr) {
  220. if (trace) {
  221. asmCode << "\tpush " << regR[instr.dst] << std::endl;
  222. }
  223. }
  224. void AssemblyGeneratorX86::traceflt(Instruction& instr) {
  225. if (trace) {
  226. asmCode << "\tpush 0" << std::endl;
  227. }
  228. }
  229. void AssemblyGeneratorX86::tracenop(Instruction& instr) {
  230. if (trace) {
  231. asmCode << "\tpush 0" << std::endl;
  232. }
  233. }
  234. void AssemblyGeneratorX86::generateCode(Instruction& instr, int i) {
  235. asmCode << "\t; " << instr;
  236. auto generator = engine[instr.opcode];
  237. (this->*generator)(instr, i);
  238. }
  239. void AssemblyGeneratorX86::genAddressReg(Instruction& instr, const char* reg = "eax") {
  240. asmCode << "\tlea " << reg << ", [" << regR32[instr.src] << std::showpos << (int32_t)instr.getImm32() << std::noshowpos << "]" << std::endl;
  241. asmCode << "\tand " << reg << ", " << ((instr.getModMem()) ? ScratchpadL1Mask : ScratchpadL2Mask) << std::endl;
  242. }
  243. void AssemblyGeneratorX86::genAddressRegDst(Instruction& instr, int maskAlign = 8) {
  244. asmCode << "\tlea eax, [" << regR32[instr.dst] << std::showpos << (int32_t)instr.getImm32() << std::noshowpos << "]" << std::endl;
  245. int mask;
  246. if (instr.getModCond()) {
  247. mask = instr.getModMem() ? ScratchpadL1Mask : ScratchpadL2Mask;
  248. }
  249. else {
  250. mask = ScratchpadL3Mask;
  251. }
  252. asmCode << "\tand eax" << ", " << (mask & (-maskAlign)) << std::endl;
  253. }
  254. int32_t AssemblyGeneratorX86::genAddressImm(Instruction& instr) {
  255. return (int32_t)instr.getImm32() & ScratchpadL3Mask;
  256. }
  257. //1 uOP
  258. void AssemblyGeneratorX86::h_IADD_RS(Instruction& instr, int i) {
  259. registerUsage[instr.dst] = i;
  260. if(instr.dst == RegisterNeedsDisplacement)
  261. asmCode << "\tlea " << regR[instr.dst] << ", [" << regR[instr.dst] << "+" << regR[instr.src] << "*" << (1 << (instr.getModShift2())) << std::showpos << (int32_t)instr.getImm32() << std::noshowpos << "]" << std::endl;
  262. else
  263. asmCode << "\tlea " << regR[instr.dst] << ", [" << regR[instr.dst] << "+" << regR[instr.src] << "*" << (1 << (instr.getModShift2())) << "]" << std::endl;
  264. traceint(instr);
  265. }
  266. //2.75 uOP
  267. void AssemblyGeneratorX86::h_IADD_M(Instruction& instr, int i) {
  268. registerUsage[instr.dst] = i;
  269. if (instr.src != instr.dst) {
  270. genAddressReg(instr);
  271. asmCode << "\tadd " << regR[instr.dst] << ", qword ptr [rsi+rax]" << std::endl;
  272. }
  273. else {
  274. asmCode << "\tadd " << regR[instr.dst] << ", qword ptr [rsi+" << genAddressImm(instr) << "]" << std::endl;
  275. }
  276. traceint(instr);
  277. }
  278. //1 uOP
  279. void AssemblyGeneratorX86::h_IADD_RC(Instruction& instr, int i) {
  280. registerUsage[instr.dst] = i;
  281. asmCode << "\tlea " << regR[instr.dst] << ", [" << regR[instr.dst] << "+" << regR[instr.src] << std::showpos << (int32_t)instr.getImm32() << std::noshowpos << "]" << std::endl;
  282. traceint(instr);
  283. }
  284. //1 uOP
  285. void AssemblyGeneratorX86::h_ISUB_R(Instruction& instr, int i) {
  286. registerUsage[instr.dst] = i;
  287. if (instr.src != instr.dst) {
  288. asmCode << "\tsub " << regR[instr.dst] << ", " << regR[instr.src] << std::endl;
  289. }
  290. else {
  291. asmCode << "\tsub " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl;
  292. }
  293. traceint(instr);
  294. }
  295. //2.75 uOP
  296. void AssemblyGeneratorX86::h_ISUB_M(Instruction& instr, int i) {
  297. registerUsage[instr.dst] = i;
  298. if (instr.src != instr.dst) {
  299. genAddressReg(instr);
  300. asmCode << "\tsub " << regR[instr.dst] << ", qword ptr [rsi+rax]" << std::endl;
  301. }
  302. else {
  303. asmCode << "\tsub " << regR[instr.dst] << ", qword ptr [rsi+" << genAddressImm(instr) << "]" << std::endl;
  304. }
  305. traceint(instr);
  306. }
  307. //1 uOP
  308. void AssemblyGeneratorX86::h_IMUL_9C(Instruction& instr, int i) {
  309. registerUsage[instr.dst] = i;
  310. asmCode << "\tlea " << regR[instr.dst] << ", [" << regR[instr.dst] << "+" << regR[instr.dst] << "*8" << std::showpos << (int32_t)instr.getImm32() << std::noshowpos << "]" << std::endl;
  311. traceint(instr);
  312. }
  313. //1 uOP
  314. void AssemblyGeneratorX86::h_IMUL_R(Instruction& instr, int i) {
  315. registerUsage[instr.dst] = i;
  316. if (instr.src != instr.dst) {
  317. asmCode << "\timul " << regR[instr.dst] << ", " << regR[instr.src] << std::endl;
  318. }
  319. else {
  320. asmCode << "\timul " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl;
  321. }
  322. traceint(instr);
  323. }
  324. //2.75 uOP
  325. void AssemblyGeneratorX86::h_IMUL_M(Instruction& instr, int i) {
  326. registerUsage[instr.dst] = i;
  327. if (instr.src != instr.dst) {
  328. genAddressReg(instr);
  329. asmCode << "\timul " << regR[instr.dst] << ", qword ptr [rsi+rax]" << std::endl;
  330. }
  331. else {
  332. asmCode << "\timul " << regR[instr.dst] << ", qword ptr [rsi+" << genAddressImm(instr) << "]" << std::endl;
  333. }
  334. traceint(instr);
  335. }
  336. //4 uOPs
  337. void AssemblyGeneratorX86::h_IMULH_R(Instruction& instr, int i) {
  338. registerUsage[instr.dst] = i;
  339. asmCode << "\tmov rax, " << regR[instr.dst] << std::endl;
  340. asmCode << "\tmul " << regR[instr.src] << std::endl;
  341. asmCode << "\tmov " << regR[instr.dst] << ", rdx" << std::endl;
  342. traceint(instr);
  343. }
  344. //5.75 uOPs
  345. void AssemblyGeneratorX86::h_IMULH_M(Instruction& instr, int i) {
  346. registerUsage[instr.dst] = i;
  347. if (instr.src != instr.dst) {
  348. genAddressReg(instr, "ecx");
  349. asmCode << "\tmov rax, " << regR[instr.dst] << std::endl;
  350. asmCode << "\tmul qword ptr [rsi+rcx]" << std::endl;
  351. }
  352. else {
  353. asmCode << "\tmov rax, " << regR[instr.dst] << std::endl;
  354. asmCode << "\tmul qword ptr [rsi+" << genAddressImm(instr) << "]" << std::endl;
  355. }
  356. asmCode << "\tmov " << regR[instr.dst] << ", rdx" << std::endl;
  357. traceint(instr);
  358. }
  359. //4 uOPs
  360. void AssemblyGeneratorX86::h_ISMULH_R(Instruction& instr, int i) {
  361. registerUsage[instr.dst] = i;
  362. asmCode << "\tmov rax, " << regR[instr.dst] << std::endl;
  363. asmCode << "\timul " << regR[instr.src] << std::endl;
  364. asmCode << "\tmov " << regR[instr.dst] << ", rdx" << std::endl;
  365. traceint(instr);
  366. }
  367. //5.75 uOPs
  368. void AssemblyGeneratorX86::h_ISMULH_M(Instruction& instr, int i) {
  369. registerUsage[instr.dst] = i;
  370. if (instr.src != instr.dst) {
  371. genAddressReg(instr, "ecx");
  372. asmCode << "\tmov rax, " << regR[instr.dst] << std::endl;
  373. asmCode << "\timul qword ptr [rsi+rcx]" << std::endl;
  374. }
  375. else {
  376. asmCode << "\tmov rax, " << regR[instr.dst] << std::endl;
  377. asmCode << "\timul qword ptr [rsi+" << genAddressImm(instr) << "]" << std::endl;
  378. }
  379. asmCode << "\tmov " << regR[instr.dst] << ", rdx" << std::endl;
  380. traceint(instr);
  381. }
  382. //1 uOP
  383. void AssemblyGeneratorX86::h_INEG_R(Instruction& instr, int i) {
  384. registerUsage[instr.dst] = i;
  385. asmCode << "\tneg " << regR[instr.dst] << std::endl;
  386. traceint(instr);
  387. }
  388. //1 uOP
  389. void AssemblyGeneratorX86::h_IXOR_R(Instruction& instr, int i) {
  390. registerUsage[instr.dst] = i;
  391. if (instr.src != instr.dst) {
  392. asmCode << "\txor " << regR[instr.dst] << ", " << regR[instr.src] << std::endl;
  393. }
  394. else {
  395. asmCode << "\txor " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl;
  396. }
  397. traceint(instr);
  398. }
  399. //2.75 uOP
  400. void AssemblyGeneratorX86::h_IXOR_M(Instruction& instr, int i) {
  401. registerUsage[instr.dst] = i;
  402. if (instr.src != instr.dst) {
  403. genAddressReg(instr);
  404. asmCode << "\txor " << regR[instr.dst] << ", qword ptr [rsi+rax]" << std::endl;
  405. }
  406. else {
  407. asmCode << "\txor " << regR[instr.dst] << ", qword ptr [rsi+" << genAddressImm(instr) << "]" << std::endl;
  408. }
  409. traceint(instr);
  410. }
  411. //1.75 uOPs
  412. void AssemblyGeneratorX86::h_IROR_R(Instruction& instr, int i) {
  413. registerUsage[instr.dst] = i;
  414. if (instr.src != instr.dst) {
  415. asmCode << "\tmov ecx, " << regR32[instr.src] << std::endl;
  416. asmCode << "\tror " << regR[instr.dst] << ", cl" << std::endl;
  417. }
  418. else {
  419. asmCode << "\tror " << regR[instr.dst] << ", " << (instr.getImm32() & 63) << std::endl;
  420. }
  421. traceint(instr);
  422. }
  423. //1.75 uOPs
  424. void AssemblyGeneratorX86::h_IROL_R(Instruction& instr, int i) {
  425. registerUsage[instr.dst] = i;
  426. if (instr.src != instr.dst) {
  427. asmCode << "\tmov ecx, " << regR32[instr.src] << std::endl;
  428. asmCode << "\trol " << regR[instr.dst] << ", cl" << std::endl;
  429. }
  430. else {
  431. asmCode << "\trol " << regR[instr.dst] << ", " << (instr.getImm32() & 63) << std::endl;
  432. }
  433. traceint(instr);
  434. }
  435. //2 uOPs
  436. void AssemblyGeneratorX86::h_IMUL_RCP(Instruction& instr, int i) {
  437. if (instr.getImm32() != 0) {
  438. registerUsage[instr.dst] = i;
  439. uint32_t divisor = instr.getImm32();
  440. asmCode << "\tmov rax, " << randomx_reciprocal(instr.getImm32()) << std::endl;
  441. asmCode << "\timul " << regR[instr.dst] << ", rax" << std::endl;
  442. traceint(instr);
  443. }
  444. else {
  445. tracenop(instr);
  446. }
  447. }
  448. //~8.5 uOPs
  449. void AssemblyGeneratorX86::h_ISDIV_C(Instruction& instr, int i) {
  450. tracenop(instr);
  451. }
  452. //2 uOPs
  453. void AssemblyGeneratorX86::h_ISWAP_R(Instruction& instr, int i) {
  454. if (instr.src != instr.dst) {
  455. registerUsage[instr.dst] = i;
  456. registerUsage[instr.src] = i;
  457. asmCode << "\txchg " << regR[instr.dst] << ", " << regR[instr.src] << std::endl;
  458. traceint(instr);
  459. }
  460. else {
  461. tracenop(instr);
  462. }
  463. }
  464. //1 uOPs
  465. void AssemblyGeneratorX86::h_FSWAP_R(Instruction& instr, int i) {
  466. asmCode << "\tshufpd " << regFE[instr.dst] << ", " << regFE[instr.dst] << ", 1" << std::endl;
  467. traceflt(instr);
  468. }
  469. //1 uOP
  470. void AssemblyGeneratorX86::h_FADD_R(Instruction& instr, int i) {
  471. instr.dst %= 4;
  472. instr.src %= 4;
  473. asmCode << "\taddpd " << regF[instr.dst] << ", " << regA[instr.src] << std::endl;
  474. traceflt(instr);
  475. }
  476. //5 uOPs
  477. void AssemblyGeneratorX86::h_FADD_M(Instruction& instr, int i) {
  478. instr.dst %= 4;
  479. genAddressReg(instr);
  480. asmCode << "\tcvtdq2pd xmm12, qword ptr [rsi+rax]" << std::endl;
  481. asmCode << "\taddpd " << regF[instr.dst] << ", xmm12" << std::endl;
  482. traceflt(instr);
  483. }
  484. //1 uOP
  485. void AssemblyGeneratorX86::h_FSUB_R(Instruction& instr, int i) {
  486. instr.dst %= 4;
  487. instr.src %= 4;
  488. asmCode << "\tsubpd " << regF[instr.dst] << ", " << regA[instr.src] << std::endl;
  489. traceflt(instr);
  490. }
  491. //5 uOPs
  492. void AssemblyGeneratorX86::h_FSUB_M(Instruction& instr, int i) {
  493. instr.dst %= 4;
  494. genAddressReg(instr);
  495. asmCode << "\tcvtdq2pd xmm12, qword ptr [rsi+rax]" << std::endl;
  496. asmCode << "\tsubpd " << regF[instr.dst] << ", xmm12" << std::endl;
  497. traceflt(instr);
  498. }
  499. //1 uOP
  500. void AssemblyGeneratorX86::h_FSCAL_R(Instruction& instr, int i) {
  501. instr.dst %= 4;
  502. asmCode << "\txorps " << regF[instr.dst] << ", " << signMask << std::endl;
  503. traceflt(instr);
  504. }
  505. //1 uOPs
  506. void AssemblyGeneratorX86::h_FMUL_R(Instruction& instr, int i) {
  507. instr.dst %= 4;
  508. instr.src %= 4;
  509. asmCode << "\tmulpd " << regE[instr.dst] << ", " << regA[instr.src] << std::endl;
  510. traceflt(instr);
  511. }
  512. //7 uOPs
  513. void AssemblyGeneratorX86::h_FMUL_M(Instruction& instr, int i) {
  514. instr.dst %= 4;
  515. genAddressReg(instr);
  516. asmCode << "\tcvtdq2pd xmm12, qword ptr [rsi+rax]" << std::endl;
  517. asmCode << "\tandps xmm12, xmm14" << std::endl;
  518. asmCode << "\tmulpd " << regE[instr.dst] << ", xmm12" << std::endl;
  519. asmCode << "\tmaxpd " << regE[instr.dst] << ", " << dblMin << std::endl;
  520. traceflt(instr);
  521. }
  522. //2 uOPs
  523. void AssemblyGeneratorX86::h_FDIV_R(Instruction& instr, int i) {
  524. instr.dst %= 4;
  525. instr.src %= 4;
  526. asmCode << "\tdivpd " << regE[instr.dst] << ", " << regA[instr.src] << std::endl;
  527. asmCode << "\tmaxpd " << regE[instr.dst] << ", " << dblMin << std::endl;
  528. traceflt(instr);
  529. }
  530. //7 uOPs
  531. void AssemblyGeneratorX86::h_FDIV_M(Instruction& instr, int i) {
  532. instr.dst %= 4;
  533. genAddressReg(instr);
  534. asmCode << "\tcvtdq2pd xmm12, qword ptr [rsi+rax]" << std::endl;
  535. asmCode << "\tandps xmm12, xmm13" << std::endl;
  536. asmCode << "\torps xmm12, xmm14" << std::endl;
  537. asmCode << "\tdivpd " << regE[instr.dst] << ", xmm12" << std::endl;
  538. traceflt(instr);
  539. }
  540. //1 uOP
  541. void AssemblyGeneratorX86::h_FSQRT_R(Instruction& instr, int i) {
  542. instr.dst %= 4;
  543. asmCode << "\tsqrtpd " << regE[instr.dst] << ", " << regE[instr.dst] << std::endl;
  544. traceflt(instr);
  545. }
  546. //6 uOPs
  547. void AssemblyGeneratorX86::h_CFROUND(Instruction& instr, int i) {
  548. asmCode << "\tmov rax, " << regR[instr.src] << std::endl;
  549. int rotate = (13 - (instr.getImm32() & 63)) & 63;
  550. if (rotate != 0)
  551. asmCode << "\trol rax, " << rotate << std::endl;
  552. asmCode << "\tand eax, 24576" << std::endl;
  553. asmCode << "\tor eax, 40896" << std::endl;
  554. asmCode << "\tmov dword ptr [rsp-8], eax" << std::endl;
  555. asmCode << "\tldmxcsr dword ptr [rsp-8]" << std::endl;
  556. tracenop(instr);
  557. }
  558. static inline const char* condition(Instruction& instr) {
  559. switch (instr.getModCond())
  560. {
  561. case 0:
  562. return "be";
  563. case 1:
  564. return "a";
  565. case 2:
  566. return "s";
  567. case 3:
  568. return "ns";
  569. case 4:
  570. return "o";
  571. case 5:
  572. return "no";
  573. case 6:
  574. return "l";
  575. case 7:
  576. return "ge";
  577. default:
  578. UNREACHABLE;
  579. }
  580. }
  581. void AssemblyGeneratorX86::handleCondition(Instruction& instr, int i) {
  582. const int shift = instr.getModShift3();
  583. const int conditionMask = ((1 << RANDOMX_CONDITION_BITS) - 1) << shift;
  584. int reg = getConditionRegister();
  585. int target = registerUsage[reg] + 1;
  586. registerUsage[reg] = i;
  587. asmCode << "\tadd " << regR[reg] << ", " << (1 << shift) << std::endl;
  588. asmCode << "\ttest " << regR[reg] << ", " << conditionMask << std::endl;
  589. asmCode << "\tjz randomx_isn_" << target << std::endl;
  590. for (unsigned j = 0; j < 8; ++j) { //mark all registers as used
  591. registerUsage[j] = i;
  592. }
  593. }
  594. //4 uOPs
  595. void AssemblyGeneratorX86::h_COND_R(Instruction& instr, int i) {
  596. handleCondition(instr, i);
  597. asmCode << "\txor ecx, ecx" << std::endl;
  598. asmCode << "\tcmp " << regR32[instr.src] << ", " << (int32_t)instr.getImm32() << std::endl;
  599. asmCode << "\tset" << condition(instr) << " cl" << std::endl;
  600. asmCode << "\tadd " << regR[instr.dst] << ", rcx" << std::endl;
  601. traceint(instr);
  602. }
  603. //6 uOPs
  604. void AssemblyGeneratorX86::h_COND_M(Instruction& instr, int i) {
  605. handleCondition(instr, i);
  606. asmCode << "\txor ecx, ecx" << std::endl;
  607. genAddressReg(instr);
  608. asmCode << "\tcmp dword ptr [rsi+rax], " << (int32_t)instr.getImm32() << std::endl;
  609. asmCode << "\tset" << condition(instr) << " cl" << std::endl;
  610. asmCode << "\tadd " << regR[instr.dst] << ", rcx" << std::endl;
  611. traceint(instr);
  612. }
  613. //3 uOPs
  614. void AssemblyGeneratorX86::h_ISTORE(Instruction& instr, int i) {
  615. genAddressRegDst(instr);
  616. asmCode << "\tmov qword ptr [rsi+rax], " << regR[instr.src] << std::endl;
  617. tracenop(instr);
  618. }
  619. //3 uOPs
  620. void AssemblyGeneratorX86::h_FSTORE(Instruction& instr, int i) {
  621. genAddressRegDst(instr, 16);
  622. asmCode << "\tmovapd xmmword ptr [rsi+rax], " << regFE[instr.src] << std::endl;
  623. tracenop(instr);
  624. }
  625. void AssemblyGeneratorX86::h_NOP(Instruction& instr, int i) {
  626. asmCode << "\tnop" << std::endl;
  627. tracenop(instr);
  628. }
  629. #include "instruction_weights.hpp"
  630. #define INST_HANDLE(x) REPN(&AssemblyGeneratorX86::h_##x, WT(x))
  631. InstructionGenerator AssemblyGeneratorX86::engine[256] = {
  632. //Integer
  633. INST_HANDLE(IADD_RS)
  634. INST_HANDLE(IADD_M)
  635. INST_HANDLE(IADD_RC)
  636. INST_HANDLE(ISUB_R)
  637. INST_HANDLE(ISUB_M)
  638. INST_HANDLE(IMUL_9C)
  639. INST_HANDLE(IMUL_R)
  640. INST_HANDLE(IMUL_M)
  641. INST_HANDLE(IMULH_R)
  642. INST_HANDLE(IMULH_M)
  643. INST_HANDLE(ISMULH_R)
  644. INST_HANDLE(ISMULH_M)
  645. INST_HANDLE(IMUL_RCP)
  646. INST_HANDLE(INEG_R)
  647. INST_HANDLE(IXOR_R)
  648. INST_HANDLE(IXOR_M)
  649. INST_HANDLE(IROR_R)
  650. INST_HANDLE(IROL_R)
  651. INST_HANDLE(ISWAP_R)
  652. //Common floating point
  653. INST_HANDLE(FSWAP_R)
  654. //Floating point group F
  655. INST_HANDLE(FADD_R)
  656. INST_HANDLE(FADD_M)
  657. INST_HANDLE(FSUB_R)
  658. INST_HANDLE(FSUB_M)
  659. INST_HANDLE(FSCAL_R)
  660. //Floating point group E
  661. INST_HANDLE(FMUL_R)
  662. INST_HANDLE(FDIV_M)
  663. INST_HANDLE(FSQRT_R)
  664. //Control
  665. INST_HANDLE(COND_R)
  666. INST_HANDLE(COND_M)
  667. INST_HANDLE(CFROUND)
  668. INST_HANDLE(ISTORE)
  669. INST_HANDLE(NOP)
  670. };
  671. }