LightProgramGenerator.cpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960
  1. /*
  2. Copyright (c) 2019 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 "blake2/blake2.h"
  16. #include "configuration.h"
  17. #include "Program.hpp"
  18. #include "blake2/endian.h";
  19. #include <iostream>
  20. #include <vector>
  21. #include <algorithm>
  22. #include <stdexcept>
  23. #include <iomanip>
  24. #include "LightProgramGenerator.hpp"
  25. namespace RandomX {
  26. // Intel Ivy Bridge reference
  27. namespace LightInstructionType { //uOPs (decode) execution ports latency code size
  28. constexpr int IADD_RS = 0; //1 p01 1 4
  29. constexpr int ISUB_R = 1; //1 p015 1 3
  30. constexpr int ISUB_C = 2; //1 p015 3 7
  31. constexpr int IMUL_R = 3; //1 p1 3 4
  32. constexpr int IMUL_C = 4; //1 p1 3 7
  33. constexpr int IMULH_R = 5; //1+2+1 0+(p1,p5)+0 3 3+3+3
  34. constexpr int ISMULH_R = 6; //1+2+1 0+(p1,p5)+0 3 3+3+3
  35. constexpr int IMUL_RCP = 7; //1+1 p015+p1 4 10+4
  36. constexpr int IXOR_R = 8; //1 p015 1 3
  37. constexpr int IXOR_C = 9; //1 p015 1 7
  38. constexpr int IROR_R = 10; //1+2 0+(p0,p5) 1 3+3
  39. constexpr int IROR_C = 11; //1 p05 1 4
  40. constexpr int COND_R = 12; //1+1+1+1+1+1 p015+p5+0+p015+p05+p015 3 7+13+3+7+3+3
  41. constexpr int COUNT = 13;
  42. }
  43. namespace LightInstructionOpcode {
  44. constexpr int IADD_RS = 0;
  45. constexpr int IADD_RC = RANDOMX_FREQ_IADD_RS + RANDOMX_FREQ_IADD_M;
  46. constexpr int ISUB_R = IADD_RC + RANDOMX_FREQ_IADD_RC;
  47. constexpr int IMUL_9C = ISUB_R + RANDOMX_FREQ_ISUB_R + RANDOMX_FREQ_ISUB_M;
  48. constexpr int IMUL_R = IMUL_9C + RANDOMX_FREQ_IMUL_9C;
  49. constexpr int IMULH_R = IMUL_R + RANDOMX_FREQ_IMUL_R + RANDOMX_FREQ_IMUL_M;
  50. constexpr int ISMULH_R = IMULH_R + RANDOMX_FREQ_IMULH_R + RANDOMX_FREQ_IMULH_M;
  51. constexpr int IMUL_RCP = ISMULH_R + RANDOMX_FREQ_ISMULH_R + RANDOMX_FREQ_ISMULH_M;
  52. constexpr int IXOR_R = IMUL_RCP + RANDOMX_FREQ_IMUL_RCP + RANDOMX_FREQ_INEG_R;
  53. constexpr int IROR_R = IXOR_R + RANDOMX_FREQ_IXOR_R + RANDOMX_FREQ_IXOR_M;
  54. constexpr int COND_R = IROR_R + RANDOMX_FREQ_IROR_R + RANDOMX_FREQ_IROL_R + RANDOMX_FREQ_ISWAP_R + RANDOMX_FREQ_FSWAP_R + RANDOMX_FREQ_FADD_R + RANDOMX_FREQ_FADD_M + RANDOMX_FREQ_FSUB_R + RANDOMX_FREQ_FSUB_M + RANDOMX_FREQ_FSCAL_R + RANDOMX_FREQ_FMUL_R + RANDOMX_FREQ_FDIV_M + RANDOMX_FREQ_FSQRT_R;
  55. }
  56. static bool isMul(int type) {
  57. return type == LightInstructionType::IMUL_R || type == LightInstructionType::IMUL_C || type == LightInstructionType::IMULH_R || type == LightInstructionType::ISMULH_R || type == LightInstructionType::IMUL_RCP;
  58. }
  59. const int lightInstructionOpcode[] = {
  60. LightInstructionOpcode::IADD_RS,
  61. LightInstructionOpcode::ISUB_R, //ISUB_R
  62. LightInstructionOpcode::ISUB_R, //ISUB_R
  63. LightInstructionOpcode::IMUL_R, //IMUL_R
  64. LightInstructionOpcode::IMUL_R, //IMUL_C
  65. LightInstructionOpcode::IMULH_R,
  66. LightInstructionOpcode::ISMULH_R,
  67. LightInstructionOpcode::IMUL_RCP,
  68. LightInstructionOpcode::IXOR_R, //IXOR_R
  69. LightInstructionOpcode::IXOR_R, //IXOR_C
  70. LightInstructionOpcode::IROR_R, //IROR_R
  71. LightInstructionOpcode::IROR_R, //IROR_C
  72. LightInstructionOpcode::COND_R
  73. };
  74. namespace ExecutionPort {
  75. using type = int;
  76. constexpr type Null = 0;
  77. constexpr type P0 = 1;
  78. constexpr type P1 = 2;
  79. constexpr type P5 = 3;
  80. constexpr type P01 = 4;
  81. constexpr type P05 = 5;
  82. constexpr type P015 = 6;
  83. }
  84. Blake2Generator::Blake2Generator(const void* seed, int nonce) : dataIndex(sizeof(data)) {
  85. memset(data, 0, sizeof(data));
  86. memcpy(data, seed, SeedSize);
  87. store32(&data[60], nonce);
  88. }
  89. uint8_t Blake2Generator::getByte() {
  90. checkData(1);
  91. return data[dataIndex++];
  92. }
  93. uint32_t Blake2Generator::getInt32() {
  94. checkData(4);
  95. auto ret = load32(&data[dataIndex]);
  96. dataIndex += 4;
  97. return ret;
  98. }
  99. void Blake2Generator::checkData(const size_t bytesNeeded) {
  100. if (dataIndex + bytesNeeded > sizeof(data)) {
  101. blake2b(data, sizeof(data), data, sizeof(data), nullptr, 0);
  102. dataIndex = 0;
  103. }
  104. }
  105. class RegisterInfo {
  106. public:
  107. RegisterInfo() : latency(0), lastOpGroup(-1), lastOpPar(-1), value(0) {}
  108. int latency;
  109. int lastOpGroup;
  110. int lastOpPar;
  111. int value;
  112. };
  113. class MacroOp {
  114. public:
  115. MacroOp(const char* name, int size)
  116. : name_(name), size_(size), latency_(0), uop1_(ExecutionPort::Null), uop2_(ExecutionPort::Null) {}
  117. MacroOp(const char* name, int size, int latency, ExecutionPort::type uop)
  118. : name_(name), size_(size), latency_(latency), uop1_(uop), uop2_(ExecutionPort::Null) {}
  119. MacroOp(const char* name, int size, int latency, ExecutionPort::type uop1, ExecutionPort::type uop2)
  120. : name_(name), size_(size), latency_(latency), uop1_(uop1), uop2_(uop2) {}
  121. MacroOp(const MacroOp& parent, bool dependent)
  122. : name_(parent.name_), size_(parent.size_), latency_(parent.latency_), uop1_(parent.uop1_), uop2_(parent.uop2_), dependent_(dependent) {}
  123. const char* getName() const {
  124. return name_;
  125. }
  126. int getSize() const {
  127. return size_;
  128. }
  129. int getLatency() const {
  130. return latency_;
  131. }
  132. ExecutionPort::type getUop1() const {
  133. return uop1_;
  134. }
  135. ExecutionPort::type getUop2() const {
  136. return uop2_;
  137. }
  138. bool isSimple() const {
  139. return uop2_ == ExecutionPort::Null;
  140. }
  141. bool isEliminated() const {
  142. return uop1_ == ExecutionPort::Null;
  143. }
  144. bool isDependent() const {
  145. return dependent_;
  146. }
  147. int getCycle() const {
  148. return cycle_;
  149. }
  150. void setCycle(int cycle) {
  151. cycle_ = cycle;
  152. }
  153. MacroOp* getSrcDep() const {
  154. return depSrc_;
  155. }
  156. void setSrcDep(MacroOp* src) {
  157. depSrc_ = src;
  158. }
  159. MacroOp* getDstDep() const {
  160. return depDst_;
  161. }
  162. void setDstDep(MacroOp* dst) {
  163. depDst_ = dst;
  164. }
  165. static const MacroOp Add_rr;
  166. static const MacroOp Add_ri;
  167. static const MacroOp Lea_sib;
  168. static const MacroOp Sub_rr;
  169. static const MacroOp Sub_ri;
  170. static const MacroOp Imul_rr;
  171. static const MacroOp Imul_rri;
  172. static const MacroOp Imul_r;
  173. static const MacroOp Mul_r;
  174. static const MacroOp Mov_rr;
  175. static const MacroOp Mov_ri64;
  176. static const MacroOp Xor_rr;
  177. static const MacroOp Xor_ri;
  178. static const MacroOp Ror_rcl;
  179. static const MacroOp Ror_ri;
  180. static const MacroOp TestJz_fused;
  181. static const MacroOp Xor_self;
  182. static const MacroOp Cmp_ri;
  183. static const MacroOp Setcc_r;
  184. private:
  185. const char* name_;
  186. int size_;
  187. int latency_;
  188. ExecutionPort::type uop1_;
  189. ExecutionPort::type uop2_;
  190. int cycle_;
  191. bool dependent_ = false;
  192. MacroOp* depDst_ = nullptr;
  193. MacroOp* depSrc_ = nullptr;
  194. };
  195. const MacroOp MacroOp::Add_rr = MacroOp("add r,r", 3, 1, ExecutionPort::P015);
  196. const MacroOp MacroOp::Add_ri = MacroOp("add r,i", 7, 1, ExecutionPort::P015);
  197. const MacroOp MacroOp::Lea_sib = MacroOp("lea r,r+r*s", 4, 1, ExecutionPort::P01);
  198. const MacroOp MacroOp::Sub_rr = MacroOp("sub r,r", 3, 1, ExecutionPort::P015);
  199. const MacroOp MacroOp::Sub_ri = MacroOp("sub r,i", 7, 1, ExecutionPort::P015);
  200. const MacroOp MacroOp::Imul_rr = MacroOp("imul r,r", 4, 3, ExecutionPort::P1);
  201. const MacroOp MacroOp::Imul_rri = MacroOp("imul r,r,i", 7, 3, ExecutionPort::P1);
  202. const MacroOp MacroOp::Imul_r = MacroOp("imul r", 3, 4, ExecutionPort::P1, ExecutionPort::P5);
  203. const MacroOp MacroOp::Mul_r = MacroOp("mul r", 3, 3, ExecutionPort::P1, ExecutionPort::P5);
  204. const MacroOp MacroOp::Mov_rr = MacroOp("mov r,r", 3);
  205. const MacroOp MacroOp::Mov_ri64 = MacroOp("mov rax,i64", 10, 1, ExecutionPort::P015);
  206. const MacroOp MacroOp::Xor_rr = MacroOp("xor r,r", 3, 1, ExecutionPort::P015);
  207. const MacroOp MacroOp::Xor_ri = MacroOp("xor r,i", 7, 1, ExecutionPort::P015);
  208. const MacroOp MacroOp::Ror_rcl = MacroOp("ror r,cl", 3, 1, ExecutionPort::P0, ExecutionPort::P5);
  209. const MacroOp MacroOp::Ror_ri = MacroOp("ror r,i", 4, 1, ExecutionPort::P05);
  210. const MacroOp MacroOp::Xor_self = MacroOp("xor rcx,rcx", 3);
  211. const MacroOp MacroOp::Cmp_ri = MacroOp("cmp r,i", 7, 1, ExecutionPort::P015);
  212. const MacroOp MacroOp::Setcc_r = MacroOp("setcc cl", 3, 1, ExecutionPort::P05);
  213. const MacroOp MacroOp::TestJz_fused = MacroOp("testjz r,i", 13, 0, ExecutionPort::P5);
  214. const MacroOp IMULH_R_ops_array[] = { MacroOp::Mov_rr, MacroOp::Mul_r, MacroOp::Mov_rr };
  215. const MacroOp ISMULH_R_ops_array[] = { MacroOp::Mov_rr, MacroOp::Imul_r, MacroOp::Mov_rr };
  216. const MacroOp IMUL_RCP_ops_array[] = { MacroOp::Mov_ri64, MacroOp(MacroOp::Imul_rr, true) };
  217. const MacroOp IROR_R_ops_array[] = { MacroOp::Mov_rr, MacroOp::Ror_rcl };
  218. const MacroOp COND_R_ops_array[] = { MacroOp::Add_ri, MacroOp(MacroOp::TestJz_fused, true), MacroOp::Xor_self, MacroOp::Cmp_ri, MacroOp(MacroOp::Setcc_r, true), MacroOp(MacroOp::Add_rr, true) };
  219. class LightInstructionInfo {
  220. public:
  221. LightInstructionInfo(const char* name, int type, const MacroOp& op, int srcOp)
  222. : name_(name), type_(type), latency_(op.getLatency()), srcOp_(srcOp) {
  223. ops_.push_back(MacroOp(op));
  224. }
  225. template <size_t N>
  226. LightInstructionInfo(const char* name, int type, const MacroOp(&arr)[N], int resultOp, int dstOp, int srcOp)
  227. : name_(name), type_(type), latency_(0), resultOp_(resultOp), dstOp_(dstOp), srcOp_(srcOp) {
  228. for (unsigned i = 0; i < N; ++i) {
  229. ops_.push_back(MacroOp(arr[i]));
  230. latency_ += ops_.back().getLatency();
  231. }
  232. static_assert(N > 1, "Invalid array size");
  233. }
  234. template <size_t N>
  235. LightInstructionInfo(const char* name, int type, const MacroOp*(&arr)[N], int latency, int resultOp, int dstOp, int srcOp)
  236. : name_(name), type_(type), latency_(latency), resultOp_(resultOp), dstOp_(dstOp), srcOp_(srcOp) {
  237. for (unsigned i = 0; i < N; ++i) {
  238. ops_.push_back(MacroOp(arr[i]));
  239. if (arr[i].isDependent()) {
  240. ops_[i].setSrcDep(&ops_[i - 1]);
  241. }
  242. }
  243. static_assert(N > 1, "Invalid array size");
  244. }
  245. const char* getName() const {
  246. return name_;
  247. }
  248. int getSize() const {
  249. return ops_.size();
  250. }
  251. bool isSimple() const {
  252. return getSize() == 1;
  253. }
  254. int getLatency() const {
  255. return latency_;
  256. }
  257. MacroOp& getOp(int index) {
  258. return ops_[index];
  259. }
  260. int getType() const {
  261. return type_;
  262. }
  263. int getResultOp() const {
  264. return resultOp_;
  265. }
  266. int getDstOp() const {
  267. return dstOp_;
  268. }
  269. int getSrcOp() const {
  270. return srcOp_;
  271. }
  272. static const LightInstructionInfo IADD_RS;
  273. static const LightInstructionInfo ISUB_R;
  274. static const LightInstructionInfo ISUB_C;
  275. static const LightInstructionInfo IMUL_R;
  276. static const LightInstructionInfo IMUL_C;
  277. static const LightInstructionInfo IMULH_R;
  278. static const LightInstructionInfo ISMULH_R;
  279. static const LightInstructionInfo IMUL_RCP;
  280. static const LightInstructionInfo IXOR_R;
  281. static const LightInstructionInfo IXOR_C;
  282. static const LightInstructionInfo IROR_R;
  283. static const LightInstructionInfo IROR_C;
  284. static const LightInstructionInfo COND_R;
  285. static const LightInstructionInfo NOP;
  286. private:
  287. const char* name_;
  288. int type_;
  289. std::vector<MacroOp> ops_;
  290. int latency_;
  291. int resultOp_ = 0;
  292. int dstOp_ = 0;
  293. int srcOp_;
  294. LightInstructionInfo(const char* name)
  295. : name_(name), type_(-1), latency_(0) {}
  296. };
  297. const LightInstructionInfo LightInstructionInfo::IADD_RS = LightInstructionInfo("IADD_RS", LightInstructionType::IADD_RS, MacroOp::Lea_sib, 0);
  298. const LightInstructionInfo LightInstructionInfo::ISUB_R = LightInstructionInfo("ISUB_R", LightInstructionType::ISUB_R, MacroOp::Sub_rr, 0);
  299. const LightInstructionInfo LightInstructionInfo::ISUB_C = LightInstructionInfo("ISUB_C", LightInstructionType::ISUB_C, MacroOp::Sub_ri, -1);
  300. const LightInstructionInfo LightInstructionInfo::IMUL_R = LightInstructionInfo("IMUL_R", LightInstructionType::IMUL_R, MacroOp::Imul_rr, 0);
  301. const LightInstructionInfo LightInstructionInfo::IMUL_C = LightInstructionInfo("IMUL_C", LightInstructionType::IMUL_C, MacroOp::Imul_rri, -1);
  302. const LightInstructionInfo LightInstructionInfo::IMULH_R = LightInstructionInfo("IMULH_R", LightInstructionType::IMULH_R, IMULH_R_ops_array, 1, 0, 1);
  303. const LightInstructionInfo LightInstructionInfo::ISMULH_R = LightInstructionInfo("ISMULH_R", LightInstructionType::ISMULH_R, ISMULH_R_ops_array, 1, 0, 1);
  304. const LightInstructionInfo LightInstructionInfo::IMUL_RCP = LightInstructionInfo("IMUL_RCP", LightInstructionType::IMUL_RCP, IMUL_RCP_ops_array, 1, 1, -1);
  305. const LightInstructionInfo LightInstructionInfo::IXOR_R = LightInstructionInfo("IXOR_R", LightInstructionType::IXOR_R, MacroOp::Xor_rr, 0);
  306. const LightInstructionInfo LightInstructionInfo::IXOR_C = LightInstructionInfo("IXOR_C", LightInstructionType::IXOR_C, MacroOp::Xor_ri, -1);
  307. const LightInstructionInfo LightInstructionInfo::IROR_R = LightInstructionInfo("IROR_R", LightInstructionType::IROR_R, IROR_R_ops_array, 1, 1, 0);
  308. const LightInstructionInfo LightInstructionInfo::IROR_C = LightInstructionInfo("IROR_C", LightInstructionType::IROR_C, MacroOp::Ror_ri, -1);
  309. const LightInstructionInfo LightInstructionInfo::COND_R = LightInstructionInfo("COND_R", LightInstructionType::COND_R, COND_R_ops_array, 5, 5, 3);
  310. const LightInstructionInfo LightInstructionInfo::NOP = LightInstructionInfo("NOP");
  311. const int buffer0[] = { 3, 3, 10 };
  312. const int buffer1[] = { 7, 3, 3, 3 };
  313. const int buffer2[] = { 3, 3, 3, 7 };
  314. const int buffer4[] = { 4, 4, 4, 4 };
  315. const int buffer5[] = { 3, 7, 3, 3 };
  316. const int buffer6[] = { 3, 3, 7, 3 };
  317. const int buffer7[] = { 13, 3 };
  318. class DecoderBuffer {
  319. public:
  320. static const DecoderBuffer Default;
  321. template <size_t N>
  322. DecoderBuffer(const char* name, int index, const int(&arr)[N])
  323. : name_(name), index_(index), counts_(arr), opsCount_(N) {}
  324. const int* getCounts() const {
  325. return counts_;
  326. }
  327. int getSize() const {
  328. return opsCount_;
  329. }
  330. int getIndex() const {
  331. return index_;
  332. }
  333. const char* getName() const {
  334. return name_;
  335. }
  336. const DecoderBuffer* fetchNext(int instrType, int cycle, int mulCount, Blake2Generator& gen) const {
  337. if (instrType == LightInstructionType::IMULH_R || instrType == LightInstructionType::ISMULH_R)
  338. return &decodeBuffer3310; //2-1-1 decode
  339. if (mulCount < cycle)
  340. return &decodeBuffer4444_mul;
  341. if (index_ == 0) {
  342. return &decodeBuffer4444; //IMUL_RCP end
  343. }
  344. /*if (index_ == 2) {
  345. return &decodeBuffer133; //COND_R middle
  346. }*/
  347. if (index_ == 7) {
  348. return &decodeBuffer7333; //COND_R end
  349. }
  350. return fetchNextDefault(gen);
  351. }
  352. private:
  353. const char* name_;
  354. int index_;
  355. const int* counts_;
  356. int opsCount_;
  357. DecoderBuffer() : index_(-1) {}
  358. static const DecoderBuffer decodeBuffer3310;
  359. static const DecoderBuffer decodeBuffer7333;
  360. static const DecoderBuffer decodeBuffer3337;
  361. static const DecoderBuffer decodeBuffer4444;
  362. static const DecoderBuffer decodeBuffer4444_mul;
  363. static const DecoderBuffer decodeBuffer3733;
  364. static const DecoderBuffer decodeBuffer3373;
  365. static const DecoderBuffer decodeBuffer133;
  366. static const DecoderBuffer* decodeBuffers[7];
  367. const DecoderBuffer* fetchNextDefault(Blake2Generator& gen) const {
  368. int select;
  369. //do {
  370. select = gen.getByte() & 3;
  371. //} while (select == 7);
  372. return decodeBuffers[select];
  373. }
  374. };
  375. const DecoderBuffer DecoderBuffer::decodeBuffer3310 = DecoderBuffer("3,3,10", 0, buffer0);
  376. const DecoderBuffer DecoderBuffer::decodeBuffer7333 = DecoderBuffer("7,3,3,3", 1, buffer1);
  377. const DecoderBuffer DecoderBuffer::decodeBuffer3337 = DecoderBuffer("3,3,3,7", 2, buffer2);
  378. const DecoderBuffer DecoderBuffer::decodeBuffer4444_mul = DecoderBuffer("4,4,4,4-MUL", 3, buffer4);
  379. const DecoderBuffer DecoderBuffer::decodeBuffer4444 = DecoderBuffer("4,4,4,4", 4, buffer4);
  380. const DecoderBuffer DecoderBuffer::decodeBuffer3733 = DecoderBuffer("3,7,3,3", 5, buffer5);
  381. const DecoderBuffer DecoderBuffer::decodeBuffer3373 = DecoderBuffer("3,3,7,3", 6, buffer6);
  382. const DecoderBuffer DecoderBuffer::decodeBuffer133 = DecoderBuffer("13,3", 7, buffer7);
  383. const DecoderBuffer* DecoderBuffer::decodeBuffers[7] = {
  384. &DecoderBuffer::decodeBuffer3310,
  385. &DecoderBuffer::decodeBuffer3337,
  386. &DecoderBuffer::decodeBuffer3733,
  387. &DecoderBuffer::decodeBuffer3373,
  388. };
  389. const DecoderBuffer DecoderBuffer::Default = DecoderBuffer();
  390. const LightInstructionInfo* slot_3[] = { &LightInstructionInfo::ISUB_R, &LightInstructionInfo::IXOR_R };
  391. const LightInstructionInfo* slot_3L[] = { &LightInstructionInfo::ISUB_R, &LightInstructionInfo::IXOR_R, &LightInstructionInfo::IMULH_R, &LightInstructionInfo::ISMULH_R };
  392. const LightInstructionInfo* slot_3C[] = { &LightInstructionInfo::ISUB_R, &LightInstructionInfo::IXOR_R, &LightInstructionInfo::IROR_R, &LightInstructionInfo::IXOR_R };
  393. const LightInstructionInfo* slot_4[] = { &LightInstructionInfo::IROR_C, &LightInstructionInfo::IADD_RS };
  394. const LightInstructionInfo* slot_7[] = { &LightInstructionInfo::IXOR_C, &LightInstructionInfo::ISUB_C };
  395. const LightInstructionInfo* slot_7L = &LightInstructionInfo::COND_R;
  396. const LightInstructionInfo* slot_10 = &LightInstructionInfo::IMUL_RCP;
  397. static bool selectRegister(std::vector<int>& availableRegisters, Blake2Generator& gen, int& reg) {
  398. int index;
  399. if (availableRegisters.size() == 0)
  400. return false;
  401. //throw std::runtime_error("No available registers");
  402. if (availableRegisters.size() > 1) {
  403. index = gen.getInt32() % availableRegisters.size();
  404. }
  405. else {
  406. index = 0;
  407. }
  408. reg = availableRegisters[index];
  409. return true;
  410. }
  411. class LightInstruction {
  412. public:
  413. void toInstr(Instruction& instr) {
  414. instr.opcode = lightInstructionOpcode[getType()];
  415. instr.dst = dst_;
  416. instr.src = src_ >= 0 ? src_ : dst_;
  417. instr.mod = mod_;
  418. instr.setImm32(imm32_);
  419. }
  420. static LightInstruction createForSlot(Blake2Generator& gen, int slotSize, int fetchType, bool isLast, bool isFirst) {
  421. switch (slotSize)
  422. {
  423. case 3:
  424. if (isLast) {
  425. return create(slot_3L[gen.getByte() & 3], gen);
  426. }
  427. else if (false && isFirst && fetchType == 0) {
  428. return create(slot_3C[gen.getByte() & 3], gen);
  429. }
  430. else {
  431. return create(slot_3[gen.getByte() & 1], gen);
  432. }
  433. case 4:
  434. if (fetchType == 3 && !isLast) {
  435. return create(&LightInstructionInfo::IMUL_R, gen);
  436. }
  437. else {
  438. return create(slot_4[gen.getByte() & 1], gen);
  439. }
  440. case 7:
  441. if (false && isLast) {
  442. return create(slot_7L, gen);
  443. }
  444. if (false && isFirst) {
  445. return create(&LightInstructionInfo::IMUL_C, gen);
  446. } else {
  447. return create(slot_7[gen.getByte() & 1], gen);
  448. }
  449. case 10:
  450. return create(slot_10, gen);
  451. default:
  452. throw std::runtime_error("Invalid slot");
  453. }
  454. }
  455. static LightInstruction create(const LightInstructionInfo* info, Blake2Generator& gen) {
  456. LightInstruction li(info);
  457. switch (info->getType())
  458. {
  459. case LightInstructionType::IADD_RS: {
  460. li.mod_ = gen.getByte();
  461. li.imm32_ = 0;
  462. li.opGroup_ = LightInstructionType::IADD_RS;
  463. li.groupParIsSource_ = true;
  464. } break;
  465. case LightInstructionType::ISUB_R: {
  466. li.mod_ = 0;
  467. li.imm32_ = 0;
  468. li.opGroup_ = LightInstructionType::IADD_RS;
  469. li.groupParIsSource_ = true;
  470. } break;
  471. case LightInstructionType::ISUB_C: {
  472. li.mod_ = 0;
  473. li.imm32_ = gen.getInt32();
  474. li.opGroup_ = LightInstructionType::ISUB_C;
  475. li.opGroupPar_ = -1;
  476. } break;
  477. case LightInstructionType::IMUL_R: {
  478. li.mod_ = 0;
  479. li.imm32_ = 0;
  480. li.opGroup_ = LightInstructionType::IMUL_R;
  481. li.opGroupPar_ = gen.getInt32();
  482. } break;
  483. case LightInstructionType::IMUL_C: {
  484. li.mod_ = 0;
  485. li.imm32_ = gen.getInt32();
  486. li.opGroup_ = LightInstructionType::IMUL_C;
  487. li.opGroupPar_ = -1;
  488. } break;
  489. case LightInstructionType::IMULH_R: {
  490. li.canReuse_ = true;
  491. li.mod_ = 0;
  492. li.imm32_ = 0;
  493. li.opGroup_ = LightInstructionType::IMULH_R;
  494. li.opGroupPar_ = gen.getInt32();
  495. } break;
  496. case LightInstructionType::ISMULH_R: {
  497. li.canReuse_ = true;
  498. li.mod_ = 0;
  499. li.imm32_ = 0;
  500. li.opGroup_ = LightInstructionType::ISMULH_R;
  501. li.opGroupPar_ = gen.getInt32();
  502. } break;
  503. case LightInstructionType::IMUL_RCP: {
  504. li.mod_ = 0;
  505. li.imm32_ = gen.getInt32();
  506. li.opGroup_ = LightInstructionType::IMUL_C;
  507. li.opGroupPar_ = -1;
  508. } break;
  509. case LightInstructionType::IXOR_R: {
  510. li.mod_ = 0;
  511. li.imm32_ = 0;
  512. li.opGroup_ = LightInstructionType::IXOR_R;
  513. li.groupParIsSource_ = true;
  514. } break;
  515. case LightInstructionType::IXOR_C: {
  516. li.mod_ = 0;
  517. li.imm32_ = gen.getInt32();
  518. li.opGroup_ = LightInstructionType::IXOR_R;
  519. li.opGroupPar_ = -1;
  520. } break;
  521. case LightInstructionType::IROR_R: {
  522. li.mod_ = 0;
  523. li.imm32_ = 0;
  524. li.opGroup_ = LightInstructionType::IROR_R;
  525. li.opGroupPar_ = -1;
  526. } break;
  527. case LightInstructionType::IROR_C: {
  528. li.mod_ = 0;
  529. do {
  530. li.imm32_ = gen.getByte();
  531. } while ((li.imm32_ & 63) == 0);
  532. li.opGroup_ = LightInstructionType::IROR_R;
  533. li.opGroupPar_ = -1;
  534. } break;
  535. case LightInstructionType::COND_R: {
  536. li.canReuse_ = true;
  537. li.mod_ = gen.getByte();
  538. li.imm32_ = gen.getInt32();
  539. li.opGroup_ = LightInstructionType::COND_R;
  540. li.opGroupPar_ = li.imm32_;
  541. } break;
  542. default:
  543. break;
  544. }
  545. return li;
  546. }
  547. bool selectDestination(int cycle, RegisterInfo (&registers)[8], Blake2Generator& gen) {
  548. std::vector<int> availableRegisters;
  549. for (unsigned i = 0; i < 8; ++i) {
  550. if (registers[i].latency <= cycle && (canReuse_ || i != src_) && (registers[i].lastOpGroup != opGroup_ || registers[i].lastOpPar != opGroupPar_) && (info_.getType() != LightInstructionType::IADD_RS || i != 5))
  551. availableRegisters.push_back(i);
  552. }
  553. return selectRegister(availableRegisters, gen, dst_);
  554. }
  555. bool selectSource(int cycle, RegisterInfo(&registers)[8], Blake2Generator& gen) {
  556. std::vector<int> availableRegisters;
  557. for (unsigned i = 0; i < 8; ++i) {
  558. if (registers[i].latency <= cycle)
  559. availableRegisters.push_back(i);
  560. }
  561. if (availableRegisters.size() == 2 && info_.getType() == LightInstructionType::IADD_RS) {
  562. if (availableRegisters[0] == 5 || availableRegisters[1] == 5) {
  563. opGroupPar_ = src_ = 5;
  564. return true;
  565. }
  566. }
  567. if (selectRegister(availableRegisters, gen, src_)) {
  568. if (groupParIsSource_)
  569. opGroupPar_ = src_;
  570. return true;
  571. }
  572. return false;
  573. }
  574. int getType() {
  575. return info_.getType();
  576. }
  577. int getSource() {
  578. return src_;
  579. }
  580. int getDestination() {
  581. return dst_;
  582. }
  583. int getGroup() {
  584. return opGroup_;
  585. }
  586. int getGroupPar() {
  587. return opGroupPar_;
  588. }
  589. LightInstructionInfo& getInfo() {
  590. return info_;
  591. }
  592. static const LightInstruction Null;
  593. private:
  594. LightInstructionInfo info_;
  595. int src_ = -1;
  596. int dst_ = -1;
  597. int mod_;
  598. uint32_t imm32_;
  599. int opGroup_;
  600. int opGroupPar_;
  601. bool canReuse_ = false;
  602. bool groupParIsSource_ = false;
  603. LightInstruction(const LightInstructionInfo* info) : info_(*info) {
  604. for (unsigned i = 0; i < info_.getSize(); ++i) {
  605. MacroOp& mop = info_.getOp(i);
  606. if (mop.isDependent()) {
  607. mop.setSrcDep(&info_.getOp(i - 1));
  608. }
  609. }
  610. }
  611. };
  612. const LightInstruction LightInstruction::Null = LightInstruction(&LightInstructionInfo::NOP);
  613. constexpr int ALU_COUNT_MUL = 1;
  614. constexpr int ALU_COUNT = 3;
  615. constexpr int LIGHT_OPCODE_BITS = 4;
  616. constexpr int V4_SRC_INDEX_BITS = 3;
  617. constexpr int V4_DST_INDEX_BITS = 3;
  618. constexpr int CYCLE_MAP_SIZE = RANDOMX_LPROG_LATENCY + 3;
  619. #ifndef _DEBUG
  620. constexpr bool TRACE = false;
  621. #else
  622. constexpr bool TRACE = true;
  623. #endif
  624. static int blakeCounter = 0;
  625. template<bool commit>
  626. static int scheduleUop(const MacroOp& mop, ExecutionPort::type(&portBusy)[CYCLE_MAP_SIZE][3], int cycle, int depCycle) {
  627. if (mop.isDependent()) {
  628. cycle = std::max(cycle, depCycle);
  629. }
  630. if (mop.isEliminated()) {
  631. if (commit)
  632. if (TRACE) std::cout << "; (eliminated)" << std::endl;
  633. return cycle;
  634. }
  635. else if (mop.isSimple()) {
  636. if (mop.getUop1() <= ExecutionPort::P5) {
  637. for (; cycle < CYCLE_MAP_SIZE; ++cycle) {
  638. if (!portBusy[cycle][mop.getUop1() - 1]) {
  639. if (commit) {
  640. if (TRACE) std::cout << "; P" << mop.getUop1() - 1 << " at cycle " << cycle << std::endl;
  641. portBusy[cycle][mop.getUop1() - 1] = mop.getUop1();
  642. }
  643. return cycle;
  644. }
  645. }
  646. }
  647. else if (mop.getUop1() == ExecutionPort::P01) {
  648. for (; cycle < CYCLE_MAP_SIZE; ++cycle) {
  649. if (!portBusy[cycle][0]) {
  650. if (commit) {
  651. if (TRACE) std::cout << "; P0 at cycle " << cycle << std::endl;
  652. portBusy[cycle][0] = mop.getUop1();
  653. }
  654. return cycle;
  655. }
  656. if (!portBusy[cycle][1]) {
  657. if (commit) {
  658. if (TRACE) std::cout << "; P1 at cycle " << cycle << std::endl;
  659. portBusy[cycle][1] = mop.getUop1();
  660. }
  661. return cycle;
  662. }
  663. }
  664. }
  665. else if (mop.getUop1() == ExecutionPort::P05) {
  666. for (; cycle < CYCLE_MAP_SIZE; ++cycle) {
  667. if (!portBusy[cycle][2]) {
  668. if (commit) {
  669. if (TRACE) std::cout << "; P2 at cycle " << cycle << std::endl;
  670. portBusy[cycle][2] = mop.getUop1();
  671. }
  672. return cycle;
  673. }
  674. if (!portBusy[cycle][0]) {
  675. if (commit) {
  676. if (TRACE) std::cout << "; P0 at cycle " << cycle << std::endl;
  677. portBusy[cycle][0] = mop.getUop1();
  678. }
  679. return cycle;
  680. }
  681. }
  682. }
  683. else {
  684. for (; cycle < CYCLE_MAP_SIZE; ++cycle) {
  685. if (!portBusy[cycle][2]) {
  686. if (commit) {
  687. if (TRACE) std::cout << "; P2 at cycle " << cycle << std::endl;
  688. portBusy[cycle][2] = mop.getUop1();
  689. }
  690. return cycle;
  691. }
  692. if (!portBusy[cycle][0]) {
  693. if (commit) {
  694. if (TRACE) std::cout << "; P0 at cycle " << cycle << std::endl;
  695. portBusy[cycle][0] = mop.getUop1();
  696. }
  697. return cycle;
  698. }
  699. if (!portBusy[cycle][1]) {
  700. if (commit) {
  701. if (TRACE) std::cout << "; P1 at cycle " << cycle << std::endl;
  702. portBusy[cycle][1] = mop.getUop1();
  703. }
  704. return cycle;
  705. }
  706. }
  707. }
  708. }
  709. else {
  710. for (; cycle < CYCLE_MAP_SIZE; ++cycle) {
  711. if (!portBusy[cycle][mop.getUop1() - 1] && !portBusy[cycle][mop.getUop2() - 1]) {
  712. if (commit) {
  713. if (TRACE) std::cout << "; P" << mop.getUop1() - 1 << " P" << mop.getUop2() - 1 << " at cycle " << cycle << std::endl;
  714. portBusy[cycle][mop.getUop1() - 1] = mop.getUop1();
  715. portBusy[cycle][mop.getUop2() - 1] = mop.getUop2();
  716. }
  717. return cycle;
  718. }
  719. }
  720. }
  721. if (TRACE) std::cout << "Unable to map operation '" << mop.getName() << "' to execution port (cycle " << cycle << ")" << std::endl;
  722. return -1;
  723. }
  724. // If we don't have enough data available, generate more
  725. static FORCE_INLINE void check_data(size_t& data_index, const size_t bytes_needed, uint8_t* data, const size_t data_size)
  726. {
  727. if (data_index + bytes_needed > data_size)
  728. {
  729. std::cout << "Calling Blake " << (++blakeCounter) << std::endl;
  730. blake2b(data, data_size, data, data_size, nullptr, 0);
  731. data_index = 0;
  732. }
  733. }
  734. double generateLightProg2(LightProgram& prog, Blake2Generator& gen) {
  735. ExecutionPort::type portBusy[CYCLE_MAP_SIZE][3];
  736. memset(portBusy, 0, sizeof(portBusy));
  737. RegisterInfo registers[8];
  738. std::vector<LightInstruction> instructions;
  739. const DecoderBuffer* fetchLine = &DecoderBuffer::Default;
  740. LightInstruction currentInstruction = LightInstruction::Null;
  741. int instrIndex = 0;
  742. int codeSize = 0;
  743. int macroOpCount = 0;
  744. int cycle = 0;
  745. int depCycle = 0;
  746. int retireCycle = 0;
  747. int mopIndex = 0;
  748. bool portsSaturated = false;
  749. int outIndex = 0;
  750. int attempts = 0;
  751. int mulCount = 0;
  752. constexpr int MAX_ATTEMPTS = 4;
  753. while(!portsSaturated) {
  754. fetchLine = fetchLine->fetchNext(currentInstruction.getType(), cycle, mulCount, gen);
  755. if (TRACE) std::cout << "; ------------- fetch cycle " << cycle << " (" << fetchLine->getName() << ")" << std::endl;
  756. mopIndex = 0;
  757. while (mopIndex < fetchLine->getSize()) {
  758. int topCycle = cycle;
  759. if (instrIndex >= currentInstruction.getInfo().getSize()) {
  760. if (portsSaturated)
  761. break;
  762. currentInstruction = LightInstruction::createForSlot(gen, fetchLine->getCounts()[mopIndex], fetchLine->getIndex(), fetchLine->getSize() == mopIndex + 1, mopIndex == 0);
  763. instrIndex = 0;
  764. if (TRACE) std::cout << "; " << currentInstruction.getInfo().getName() << std::endl;
  765. }
  766. MacroOp& mop = currentInstruction.getInfo().getOp(instrIndex);
  767. if (fetchLine->getCounts()[mopIndex] != mop.getSize()) {
  768. if (TRACE) std::cout << "ERROR instruction " << mop.getName() << " doesn't fit into slot of size " << fetchLine->getCounts()[mopIndex] << std::endl;
  769. return DBL_MIN;
  770. }
  771. if (TRACE) std::cout << mop.getName() << " ";
  772. int scheduleCycle = scheduleUop<false>(mop, portBusy, cycle, depCycle);
  773. mop.setCycle(scheduleCycle);
  774. if (scheduleCycle < 0) {
  775. if (TRACE) std::cout << "; Failed at cycle " << cycle << std::endl;
  776. return DBL_MIN;
  777. }
  778. if (instrIndex == currentInstruction.getInfo().getSrcOp()) {
  779. for (attempts = 0; attempts < MAX_ATTEMPTS && !currentInstruction.selectSource(scheduleCycle, registers, gen); ++attempts) {
  780. if (TRACE) std::cout << "; src STALL at cycle " << cycle << std::endl;
  781. ++scheduleCycle;
  782. ++cycle;
  783. }
  784. if (attempts == MAX_ATTEMPTS) { //throw instruction away
  785. //cycle = topCycle;
  786. instrIndex = currentInstruction.getInfo().getSize();
  787. if (TRACE) std::cout << "; THROW away " << currentInstruction.getInfo().getName() << std::endl;
  788. continue;
  789. }
  790. if (TRACE) std::cout << "; src = r" << currentInstruction.getSource() << std::endl;
  791. }
  792. if (instrIndex == currentInstruction.getInfo().getDstOp()) {
  793. for (attempts = 0; attempts < MAX_ATTEMPTS && !currentInstruction.selectDestination(scheduleCycle, registers, gen); ++attempts) {
  794. if (TRACE) std::cout << "; dst STALL at cycle " << cycle << std::endl;
  795. ++scheduleCycle;
  796. ++cycle;
  797. }
  798. if (attempts == MAX_ATTEMPTS) { //throw instruction away
  799. //cycle = topCycle;
  800. instrIndex = currentInstruction.getInfo().getSize();
  801. if (TRACE) std::cout << "; THROW away " << currentInstruction.getInfo().getName() << std::endl;
  802. continue;
  803. }
  804. if (TRACE) std::cout << "; dst = r" << currentInstruction.getDestination() << std::endl;
  805. }
  806. scheduleCycle = scheduleUop<true>(mop, portBusy, scheduleCycle, scheduleCycle);
  807. depCycle = scheduleCycle + mop.getLatency();
  808. if (instrIndex == currentInstruction.getInfo().getResultOp()) {
  809. int dst = currentInstruction.getDestination();
  810. RegisterInfo& ri = registers[dst];
  811. retireCycle = depCycle;
  812. ri.latency = retireCycle;
  813. ri.lastOpGroup = currentInstruction.getGroup();
  814. ri.lastOpPar = currentInstruction.getGroupPar();
  815. if (TRACE) std::cout << "; RETIRED at cycle " << retireCycle << std::endl;
  816. }
  817. codeSize += mop.getSize();
  818. mopIndex++;
  819. instrIndex++;
  820. macroOpCount++;
  821. if (scheduleCycle >= RANDOMX_LPROG_LATENCY) {
  822. portsSaturated = true;
  823. }
  824. cycle = topCycle;
  825. if (instrIndex >= currentInstruction.getInfo().getSize()) {
  826. currentInstruction.toInstr(prog(outIndex++));
  827. mulCount += isMul(currentInstruction.getType());
  828. }
  829. }
  830. ++cycle;
  831. }
  832. std::cout << "; ALU port utilization:" << std::endl;
  833. std::cout << "; (* = in use, _ = idle)" << std::endl;
  834. int portCycles = 0;
  835. /*for (int i = 0; i < CYCLE_MAP_SIZE; ++i) {
  836. std::cout << "; " << std::setw(3) << i << " ";
  837. for (int j = 0; j < 3; ++j) {
  838. std::cout << (portBusy[i][j] ? '*' : '_');
  839. portCycles += !!portBusy[i][j];
  840. }
  841. std::cout << std::endl;
  842. }*/
  843. double ipc = (macroOpCount / (double)retireCycle);
  844. std::cout << "; code size " << codeSize << " bytes" << std::endl;
  845. std::cout << "; x86 macro-ops: " << macroOpCount << std::endl;
  846. std::cout << "; RandomX instructions: " << outIndex << std::endl;
  847. std::cout << "; Execution time: " << retireCycle << " cycles" << std::endl;
  848. std::cout << "; IPC = " << ipc << std::endl;
  849. std::cout << "; Port-cycles: " << portCycles << std::endl;
  850. std::cout << "; Multiplications: " << mulCount << std::endl;
  851. int asicLatency[8];
  852. memset(asicLatency, 0, sizeof(asicLatency));
  853. for (int i = 0; i < outIndex; ++i) {
  854. Instruction& instr = prog(i);
  855. int latDst = asicLatency[instr.dst] + 1;
  856. int latSrc = instr.dst != instr.src ? asicLatency[instr.src] + 1 : 0;
  857. asicLatency[instr.dst] = std::max(latDst, latSrc);
  858. }
  859. int asicLatencyFinal = 0;
  860. int addressReg = 0;
  861. for (int i = 0; i < 8; ++i) {
  862. if (asicLatency[i] > asicLatencyFinal) {
  863. asicLatencyFinal = asicLatency[i];
  864. addressReg = i;
  865. }
  866. }
  867. std::cout << "; ASIC latency: " << asicLatencyFinal << std::endl;
  868. std::cout << "; ASIC latency:" << std::endl;
  869. for (int i = 0; i < 8; ++i) {
  870. std::cout << "; r" << i << " = " << asicLatency[i] << std::endl;
  871. }
  872. std::cout << "; CPU latency:" << std::endl;
  873. for (int i = 0; i < 8; ++i) {
  874. std::cout << "; r" << i << " = " << registers[i].latency << std::endl;
  875. }
  876. prog.setSize(outIndex);
  877. prog.setAddressRegister(addressReg);
  878. return addressReg;
  879. }
  880. }