| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387 |
- /*
- Copyright (c) 2018 tevador
- This file is part of RandomX.
- RandomX is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- RandomX is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with RandomX. If not, see<http://www.gnu.org/licenses/>.
- */
- //#define TRACE
- //#define FPUCHECK
- #include "InterpretedVirtualMachine.hpp"
- #include "Pcg32.hpp"
- #include "instructions.hpp"
- #include <iostream>
- #include <iomanip>
- #include <stdexcept>
- #include <sstream>
- #include <cmath>
- #ifdef STATS
- #include <algorithm>
- #endif
- #ifdef FPUCHECK
- constexpr bool fpuCheck = true;
- #else
- constexpr bool fpuCheck = false;
- #endif
- namespace RandomX {
- void InterpretedVirtualMachine::initializeProgram(const void* seed) {
- Pcg32 gen(seed);
- for (unsigned i = 0; i < sizeof(reg) / sizeof(Pcg32::result_type); ++i) {
- *(((uint32_t*)®) + i) = gen();
- }
- FPINIT();
- for (int i = 0; i < RegistersCount; ++i) {
- reg.f[i].lo.f64 = (double)reg.f[i].lo.i64;
- reg.f[i].hi.f64 = (double)reg.f[i].hi.i64;
- }
- //std::cout << reg;
- p.initialize(gen);
- mem.ma = (gen() ^ *(((uint32_t*)seed) + 4)) & ~7;
- mem.mx = *(((uint32_t*)seed) + 5);
- pc = 0;
- ic = InstructionCount;
- stack.clear();
- }
- void InterpretedVirtualMachine::execute() {
- while (ic > 0) {
- #ifdef STATS
- count_instructions[pc]++;
- #endif
- auto& inst = p(pc);
- if(trace) std::cout << inst.getName() << " (" << std::dec << pc << ")" << std::endl;
- pc = (pc + 1) % ProgramLength;
- auto handler = engine[inst.opcode];
- (this->*handler)(inst);
- ic--;
- }
- #ifdef STATS
- count_endstack += stack.size();
- #endif
- }
- convertible_t InterpretedVirtualMachine::loada(Instruction& inst) {
- convertible_t& rega = reg.r[inst.rega % RegistersCount];
- rega.i64 ^= inst.addra; //sign-extend addra
- addr_t addr = rega.u32;
- switch (inst.loca & 7)
- {
- case 0:
- case 1:
- case 2:
- case 3:
- return readDataset(addr, mem);
- case 4:
- return scratchpad[addr % ScratchpadL2];
- case 5:
- case 6:
- case 7:
- return scratchpad[addr % ScratchpadL1];
- }
- }
- convertible_t InterpretedVirtualMachine::loadbr1(Instruction& inst) {
- switch (inst.locb & 7)
- {
- case 0:
- case 1:
- case 2:
- case 3:
- case 4:
- case 5:
- return reg.r[inst.regb % RegistersCount];
- case 6:
- case 7:
- convertible_t temp;
- temp.i64 = inst.imm32; //sign-extend imm32
- return temp;
- }
- }
- convertible_t InterpretedVirtualMachine::loadbr0(Instruction& inst) {
- switch (inst.locb & 7)
- {
- case 0:
- case 1:
- case 2:
- case 3:
- return reg.r[inst.regb % RegistersCount];
- case 4:
- case 5:
- case 6:
- case 7:
- convertible_t temp;
- temp.u64 = inst.imm8;
- return temp;
- }
- }
- convertible_t& InterpretedVirtualMachine::getcr(Instruction& inst) {
- addr_t addr;
- switch (inst.locc & 7)
- {
- case 0:
- addr = reg.r[inst.regc % RegistersCount].u32 ^ inst.addrc;
- return scratchpad[addr % ScratchpadL2];
- case 1:
- case 2:
- case 3:
- addr = reg.r[inst.regc % RegistersCount].u32 ^ inst.addrc;
- return scratchpad[addr % ScratchpadL1];
- case 4:
- case 5:
- case 6:
- case 7:
- return reg.r[inst.regc % RegistersCount];
- }
- }
- void InterpretedVirtualMachine::writecf(Instruction& inst, fpu_reg_t& regc) {
- addr_t addr;
- switch (inst.locc & 7)
- {
- case 4:
- addr = reg.r[inst.regc % RegistersCount].u32 ^ inst.addrc;
- scratchpad[addr % ScratchpadL2] = (inst.locc & 8) ? regc.hi : regc.lo;
- break;
- case 5:
- case 6:
- case 7:
- addr = reg.r[inst.regc % RegistersCount].u32 ^ inst.addrc;
- scratchpad[addr % ScratchpadL1] = (inst.locc & 8) ? regc.hi : regc.lo;
- default:
- break;
- }
- }
- void InterpretedVirtualMachine::writecflo(Instruction& inst, fpu_reg_t& regc) {
- addr_t addr;
- switch (inst.locc & 7)
- {
- case 4:
- addr = reg.r[inst.regc % RegistersCount].u32 ^ inst.addrc;
- scratchpad[addr % ScratchpadL2] = regc.lo;
- break;
- case 5:
- case 6:
- case 7:
- addr = reg.r[inst.regc % RegistersCount].u32 ^ inst.addrc;
- scratchpad[addr % ScratchpadL1] = regc.lo;
- default:
- break;
- }
- }
- #define ALU_RETIRE(x) x(a, b, c); \
- if(trace) std::cout << std::hex << /*a.u64 << " " << b.u64 << " " <<*/ c.u64 << std::endl;
- #define FPU_RETIRE(x) x(a, b, c); \
- writecf(inst, c); \
- if(trace) { \
- std::cout << std::hex << ((inst.locc & 8) ? c.hi.u64 : c.lo.u64) << std::endl; \
- } \
- if(fpuCheck) { \
- if(c.hi.f64 != c.hi.f64 || c.lo.f64 != c.lo.f64) { \
- std::stringstream ss; \
- ss << "NaN result of " << #x << "(" << std::hex << a.u64 << ", " << b.hi.u64 << " " << b.lo.u64 << ") = " << c.hi.u64 << " " << c.lo.u64 << std::endl; \
- throw std::runtime_error(ss.str()); \
- } else if (std::fpclassify(c.hi.f64) == FP_SUBNORMAL || std::fpclassify(c.lo.f64) == FP_SUBNORMAL) {\
- std::stringstream ss; \
- ss << "Denormal result of " << #x << "(" << std::hex << a.u64 << ", " << b.hi.u64 << " " << b.lo.u64 << ") = " << c.hi.u64 << " " << c.lo.u64 << std::endl; \
- throw std::runtime_error(ss.str()); \
- } \
- }
- #ifdef STATS
- #define INC_COUNT(x) count_##x++;
- #else
- #define INC_COUNT(x)
- #endif
- #define FPU_RETIRE_FPSQRT(x) FPSQRT(a, b, c); \
- writecf(inst, c); \
- if(trace) std::cout << std::hex << ((inst.locc & 8) ? c.hi.u64 : c.lo.u64) << std::endl;
- #define FPU_RETIRE_FPROUND(x) FPROUND(a, b, c); \
- writecflo(inst, c); \
- if(trace) std::cout << std::hex << c.lo.u64 << std::endl;
- #define ALU_INST(x) void InterpretedVirtualMachine::h_##x(Instruction& inst) { \
- INC_COUNT(x) \
- convertible_t a = loada(inst); \
- convertible_t b = loadbr1(inst); \
- convertible_t& c = getcr(inst); \
- ALU_RETIRE(x) \
- }
- #define ALU_INST_SR(x) void InterpretedVirtualMachine::h_##x(Instruction& inst) { \
- INC_COUNT(x) \
- convertible_t a = loada(inst); \
- convertible_t b = loadbr0(inst); \
- convertible_t& c = getcr(inst); \
- ALU_RETIRE(x) \
- }
- #define FPU_INST(x) void InterpretedVirtualMachine::h_##x(Instruction& inst) { \
- INC_COUNT(x) \
- convertible_t a = loada(inst); \
- fpu_reg_t& b = reg.f[inst.regb % RegistersCount]; \
- fpu_reg_t& c = reg.f[inst.regc % RegistersCount]; \
- FPU_RETIRE(x) \
- }
- #define FPU_INST_NB(x) void InterpretedVirtualMachine::h_##x(Instruction& inst) { \
- INC_COUNT(x) \
- convertible_t a = loada(inst); \
- fpu_reg_t b; \
- fpu_reg_t& c = reg.f[inst.regc % RegistersCount]; \
- FPU_RETIRE_##x(x) \
- }
- ALU_INST(ADD_64)
- ALU_INST(ADD_32)
- ALU_INST(SUB_64)
- ALU_INST(SUB_32)
- ALU_INST(MUL_64)
- ALU_INST(MULH_64)
- ALU_INST(MUL_32)
- ALU_INST(IMUL_32)
- ALU_INST(IMULH_64)
- ALU_INST(DIV_64)
- ALU_INST(IDIV_64)
- ALU_INST(AND_64)
- ALU_INST(AND_32)
- ALU_INST(OR_64)
- ALU_INST(OR_32)
- ALU_INST(XOR_64)
- ALU_INST(XOR_32)
- ALU_INST_SR(SHL_64)
- ALU_INST_SR(SHR_64)
- ALU_INST_SR(SAR_64)
- ALU_INST_SR(ROL_64)
- ALU_INST_SR(ROR_64)
- FPU_INST(FPADD)
- FPU_INST(FPSUB)
- FPU_INST(FPMUL)
- FPU_INST(FPDIV)
- FPU_INST_NB(FPSQRT)
- FPU_INST_NB(FPROUND)
- void InterpretedVirtualMachine::h_CALL(Instruction& inst) {
- convertible_t a = loada(inst);
- if (JMP_COND(inst.locb, reg.r[inst.regb % RegistersCount], inst.imm32)) {
- #ifdef STATS
- count_CALL_taken++;
- count_jump_taken[inst.locb & 7]++;
- count_retdepth = std::max(0, count_retdepth - 1);
- #endif
- stackPush(a);
- stackPush(pc);
- #ifdef STATS
- count_max_stack = std::max(count_max_stack, (int)stack.size());
- #endif
- pc += (inst.imm8 & 127) + 1;
- pc = pc % ProgramLength;
- if (trace) std::cout << std::hex << a.u64 << std::endl;
- }
- else {
- convertible_t& c = getcr(inst);
- #ifdef STATS
- count_CALL_not_taken++;
- count_jump_not_taken[inst.locb & 7]++;
- #endif
- c.u64 = a.u64;
- if (trace) std::cout << std::hex << /*a.u64 << " " <<*/ c.u64 << std::endl;
- }
- }
- void InterpretedVirtualMachine::h_RET(Instruction& inst) {
- convertible_t a = loada(inst);
- convertible_t b = loadbr1(inst);
- convertible_t& c = getcr(inst);
- if (stack.size() > 0) {
- #ifdef STATS
- count_RET_taken++;
- count_retdepth++;
- count_retdepth_max = std::max(count_retdepth_max, count_retdepth);
- #endif
- auto raddr = stackPopAddress();
- auto retval = stackPopValue();
- c.u64 = a.u64 ^ retval.u64;
- pc = raddr;
- }
- else {
- #ifdef STATS
- if (stack.size() == 0)
- count_RET_stack_empty++;
- else {
- count_RET_not_taken++;
- count_jump_not_taken[inst.locb & 7]++;
- }
- #endif
- c.u64 = a.u64;
- }
- if (trace) std::cout << std::hex << /*a.u64 << " " <<*/ c.u64 << std::endl;
- }
- #include "instructionWeights.hpp"
- #define INST_HANDLE(x) REPN(&InterpretedVirtualMachine::h_##x, WT(x))
- InstructionHandler InterpretedVirtualMachine::engine[256] = {
- INST_HANDLE(ADD_64)
- INST_HANDLE(ADD_32)
- INST_HANDLE(SUB_64)
- INST_HANDLE(SUB_32)
- INST_HANDLE(MUL_64)
- INST_HANDLE(MULH_64)
- INST_HANDLE(MUL_32)
- INST_HANDLE(IMUL_32)
- INST_HANDLE(IMULH_64)
- INST_HANDLE(DIV_64)
- INST_HANDLE(IDIV_64)
- INST_HANDLE(AND_64)
- INST_HANDLE(AND_32)
- INST_HANDLE(OR_64)
- INST_HANDLE(OR_32)
- INST_HANDLE(XOR_64)
- INST_HANDLE(XOR_32)
- INST_HANDLE(SHL_64)
- INST_HANDLE(SHR_64)
- INST_HANDLE(SAR_64)
- INST_HANDLE(ROL_64)
- INST_HANDLE(ROR_64)
- INST_HANDLE(FPADD)
- INST_HANDLE(FPSUB)
- INST_HANDLE(FPMUL)
- INST_HANDLE(FPDIV)
- INST_HANDLE(FPSQRT)
- INST_HANDLE(FPROUND)
- INST_HANDLE(CALL)
- INST_HANDLE(RET)
- };
- }
|