InterpretedVirtualMachine.cpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  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. #include "InterpretedVirtualMachine.hpp"
  18. #include "Pcg32.hpp"
  19. #include "instructions.hpp"
  20. #include <iostream>
  21. #include <iomanip>
  22. #include <stdexcept>
  23. #include <sstream>
  24. #include <cmath>
  25. #ifdef FPUCHECK
  26. constexpr bool fpuCheck = true;
  27. #else
  28. constexpr bool fpuCheck = false;
  29. #endif
  30. namespace RandomX {
  31. void InterpretedVirtualMachine::initializeProgram(const void* seed) {
  32. Pcg32 gen(seed);
  33. for (unsigned i = 0; i < sizeof(reg) / sizeof(Pcg32::result_type); ++i) {
  34. *(((uint32_t*)&reg) + i) = gen();
  35. }
  36. FPINIT();
  37. for (int i = 0; i < 8; ++i) {
  38. reg.f[i].f64 = (double)reg.f[i].i64;
  39. }
  40. p.initialize(gen);
  41. mem.ma = (gen() ^ *(((uint32_t*)seed) + 4)) & ~7;
  42. mem.mx = *(((uint32_t*)seed) + 5);
  43. pc = 0;
  44. ic = InstructionCount;
  45. stack.clear();
  46. }
  47. void InterpretedVirtualMachine::execute() {
  48. while (ic > 0) {
  49. auto& inst = p(pc);
  50. if(trace) std::cout << inst.getName() << " (" << std::dec << pc << ")" << std::endl;
  51. pc = (pc + 1) % ProgramLength;
  52. auto handler = engine[inst.opcode];
  53. (this->*handler)(inst);
  54. ic--;
  55. }
  56. }
  57. convertible_t InterpretedVirtualMachine::loada(Instruction& inst) {
  58. convertible_t& rega = reg.r[inst.rega % RegistersCount];
  59. rega.i64 ^= inst.addra; //sign-extend addra
  60. addr_t addr = rega.u32;
  61. switch (inst.loca & 7)
  62. {
  63. case 0:
  64. case 1:
  65. case 2:
  66. case 3:
  67. return readDataset(addr, mem);
  68. case 4:
  69. return scratchpad[addr % ScratchpadL2];
  70. case 5:
  71. case 6:
  72. case 7:
  73. return scratchpad[addr % ScratchpadL1];
  74. }
  75. }
  76. convertible_t InterpretedVirtualMachine::loadbr1(Instruction& inst) {
  77. switch (inst.locb & 7)
  78. {
  79. case 0:
  80. case 1:
  81. case 2:
  82. case 3:
  83. case 4:
  84. case 5:
  85. return reg.r[inst.regb % RegistersCount];
  86. case 6:
  87. case 7:
  88. convertible_t temp;
  89. temp.i64 = inst.imm32; //sign-extend imm32
  90. return temp;
  91. }
  92. }
  93. convertible_t InterpretedVirtualMachine::loadbr0(Instruction& inst) {
  94. switch (inst.locb & 7)
  95. {
  96. case 0:
  97. case 1:
  98. case 2:
  99. case 3:
  100. case 4:
  101. case 5:
  102. return reg.r[inst.regb % RegistersCount];
  103. case 6:
  104. case 7:
  105. convertible_t temp;
  106. temp.u64 = inst.imm8;
  107. return temp;
  108. }
  109. }
  110. double InterpretedVirtualMachine::loadbf(Instruction& inst) {
  111. switch (inst.locb & 7)
  112. {
  113. case 0:
  114. case 1:
  115. case 2:
  116. case 3:
  117. case 4:
  118. case 5:
  119. return reg.f[inst.regb % RegistersCount].f64;
  120. case 6:
  121. case 7:
  122. return (double)inst.imm32;
  123. }
  124. }
  125. convertible_t& InterpretedVirtualMachine::getcr(Instruction& inst) {
  126. addr_t addr;
  127. switch (inst.locc & 7)
  128. {
  129. case 0:
  130. addr = reg.r[inst.regc % RegistersCount].u32 ^ inst.addrc;
  131. return scratchpad[addr % ScratchpadL2];
  132. case 1:
  133. case 2:
  134. case 3:
  135. addr = reg.r[inst.regc % RegistersCount].u32 ^ inst.addrc;
  136. return scratchpad[addr % ScratchpadL1];
  137. case 4:
  138. case 5:
  139. case 6:
  140. case 7:
  141. return reg.r[inst.regc % RegistersCount];
  142. }
  143. }
  144. convertible_t& InterpretedVirtualMachine::getcf(Instruction& inst) {
  145. addr_t addr;
  146. switch (inst.locc & 7)
  147. {
  148. case 0:
  149. addr = reg.r[inst.regc % RegistersCount].u32 ^ inst.addrc;
  150. return scratchpad[addr % ScratchpadL2];
  151. case 1:
  152. case 2:
  153. case 3:
  154. addr = reg.r[inst.regc % RegistersCount].u32 ^ inst.addrc;
  155. return scratchpad[addr % ScratchpadL1];
  156. case 4:
  157. case 5:
  158. case 6:
  159. case 7:
  160. return reg.f[inst.regc % RegistersCount];
  161. }
  162. }
  163. #define ALU_RETIRE(x) x(a, b, c); \
  164. if(trace) std::cout << std::hex << /*a.u64 << " " << b.u64 << " " <<*/ c.u64 << std::endl;
  165. #define FPU_RETIRE(x) x(a, b, c); \
  166. if(trace) { \
  167. convertible_t bc; \
  168. bc.f64 = b; \
  169. std::cout << std::hex << /*a.u64 << " " << bc.u64 << " " <<*/ c.u64 << std::endl; \
  170. } \
  171. if(fpuCheck) { \
  172. convertible_t bc; \
  173. if(c.f64 != c.f64) { \
  174. std::stringstream ss; \
  175. bc.f64 = b; \
  176. ss << "NaN result of " << #x << "(" << std::hex << a.u64 << ", " << bc.u64 << ") = " << c.u64; \
  177. throw std::runtime_error(ss.str()); \
  178. } else if (std::fpclassify(c.f64) == FP_SUBNORMAL) {\
  179. std::stringstream ss; \
  180. bc.f64 = b; \
  181. ss << "Denormal result of " << #x << "(" << std::hex << a.u64 << ", " << bc.u64 << ") = " << c.u64; \
  182. throw std::runtime_error(ss.str()); \
  183. } \
  184. }
  185. #define FPU_RETIRE_NB(x) x(a, b, c); \
  186. if(trace) std::cout << std::hex << /*a.u64 << " " <<*/ c.u64 << std::endl;
  187. #define ALU_INST(x) void InterpretedVirtualMachine::h_##x(Instruction& inst) { \
  188. convertible_t a = loada(inst); \
  189. convertible_t b = loadbr1(inst); \
  190. convertible_t& c = getcr(inst); \
  191. ALU_RETIRE(x) \
  192. }
  193. #define ALU_INST_SR(x) void InterpretedVirtualMachine::h_##x(Instruction& inst) { \
  194. convertible_t a = loada(inst); \
  195. convertible_t b = loadbr0(inst); \
  196. convertible_t& c = getcr(inst); \
  197. ALU_RETIRE(x) \
  198. }
  199. #define FPU_INST(x) void InterpretedVirtualMachine::h_##x(Instruction& inst) { \
  200. convertible_t a = loada(inst); \
  201. double b = loadbf(inst); \
  202. convertible_t& c = getcf(inst); \
  203. FPU_RETIRE(x) \
  204. }
  205. #define FPU_INST_NB(x) void InterpretedVirtualMachine::h_##x(Instruction& inst) { \
  206. convertible_t a = loada(inst); \
  207. convertible_t b; \
  208. convertible_t& c = getcf(inst); \
  209. FPU_RETIRE_NB(x) \
  210. }
  211. ALU_INST(ADD_64)
  212. ALU_INST(ADD_32)
  213. ALU_INST(SUB_64)
  214. ALU_INST(SUB_32)
  215. ALU_INST(MUL_64)
  216. ALU_INST(MULH_64)
  217. ALU_INST(MUL_32)
  218. ALU_INST(IMUL_32)
  219. ALU_INST(IMULH_64)
  220. ALU_INST(DIV_64)
  221. ALU_INST(IDIV_64)
  222. ALU_INST(AND_64)
  223. ALU_INST(AND_32)
  224. ALU_INST(OR_64)
  225. ALU_INST(OR_32)
  226. ALU_INST(XOR_64)
  227. ALU_INST(XOR_32)
  228. ALU_INST_SR(SHL_64)
  229. ALU_INST_SR(SHR_64)
  230. ALU_INST_SR(SAR_64)
  231. ALU_INST_SR(ROL_64)
  232. ALU_INST_SR(ROR_64)
  233. FPU_INST(FPADD)
  234. FPU_INST(FPSUB)
  235. FPU_INST(FPMUL)
  236. FPU_INST(FPDIV)
  237. FPU_INST_NB(FPSQRT)
  238. FPU_INST_NB(FPROUND)
  239. void InterpretedVirtualMachine::h_CALL(Instruction& inst) {
  240. convertible_t a = loada(inst);
  241. convertible_t b = loadbr1(inst);
  242. convertible_t& c = getcr(inst);
  243. if (b.u32 <= (uint32_t)inst.imm32) {
  244. stackPush(a);
  245. stackPush(pc);
  246. pc += (inst.imm8 & 127) + 1;
  247. pc = pc % ProgramLength;
  248. if (trace) std::cout << std::hex << a.u64 << std::endl;
  249. }
  250. else {
  251. c.u64 = a.u64;
  252. if (trace) std::cout << std::hex << /*a.u64 << " " <<*/ c.u64 << std::endl;
  253. }
  254. }
  255. void InterpretedVirtualMachine::h_RET(Instruction& inst) {
  256. convertible_t a = loada(inst);
  257. convertible_t b = loadbr1(inst);
  258. convertible_t& c = getcr(inst);
  259. if (stack.size() > 0 && b.u32 <= (uint32_t)inst.imm32) {
  260. auto raddr = stackPopAddress();
  261. auto retval = stackPopValue();
  262. c.u64 = a.u64 ^ retval.u64;
  263. pc = raddr;
  264. }
  265. else {
  266. c.u64 = a.u64;
  267. }
  268. if (trace) std::cout << std::hex << /*a.u64 << " " <<*/ c.u64 << std::endl;
  269. }
  270. #include "instructionWeights.hpp"
  271. #define INST_HANDLE(x) REPN(&InterpretedVirtualMachine::h_##x, WT(x))
  272. InstructionHandler InterpretedVirtualMachine::engine[256] = {
  273. INST_HANDLE(ADD_64)
  274. INST_HANDLE(ADD_32)
  275. INST_HANDLE(SUB_64)
  276. INST_HANDLE(SUB_32)
  277. INST_HANDLE(MUL_64)
  278. INST_HANDLE(MULH_64)
  279. INST_HANDLE(MUL_32)
  280. INST_HANDLE(IMUL_32)
  281. INST_HANDLE(IMULH_64)
  282. INST_HANDLE(DIV_64)
  283. INST_HANDLE(IDIV_64)
  284. INST_HANDLE(AND_64)
  285. INST_HANDLE(AND_32)
  286. INST_HANDLE(OR_64)
  287. INST_HANDLE(OR_32)
  288. INST_HANDLE(XOR_64)
  289. INST_HANDLE(XOR_32)
  290. INST_HANDLE(SHL_64)
  291. INST_HANDLE(SHR_64)
  292. INST_HANDLE(SAR_64)
  293. INST_HANDLE(ROL_64)
  294. INST_HANDLE(ROR_64)
  295. INST_HANDLE(FPADD)
  296. INST_HANDLE(FPSUB)
  297. INST_HANDLE(FPMUL)
  298. INST_HANDLE(FPDIV)
  299. INST_HANDLE(FPSQRT)
  300. INST_HANDLE(FPROUND)
  301. INST_HANDLE(CALL)
  302. INST_HANDLE(RET)
  303. };
  304. }