C API - first working version

This commit is contained in:
tevador 2019-04-20 11:08:01 +02:00
parent 67046a9f38
commit 296e77eebc
36 changed files with 1286 additions and 938 deletions

View File

@ -25,7 +25,7 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
#include "Program.hpp" #include "Program.hpp"
#include "superscalarGenerator.hpp" #include "superscalarGenerator.hpp"
namespace RandomX { namespace randomx {
static const char* regR[8] = { "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" }; static const char* regR[8] = { "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" };
static const char* regR32[8] = { "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" }; static const char* regR32[8] = { "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" };
@ -69,54 +69,54 @@ namespace RandomX {
Instruction& instr = prog(i); Instruction& instr = prog(i);
switch (instr.opcode) switch (instr.opcode)
{ {
case RandomX::SuperscalarInstructionType::ISUB_R: case SuperscalarInstructionType::ISUB_R:
asmCode << "sub " << regR[instr.dst] << ", " << regR[instr.src] << std::endl; asmCode << "sub " << regR[instr.dst] << ", " << regR[instr.src] << std::endl;
break; break;
case RandomX::SuperscalarInstructionType::IXOR_R: case SuperscalarInstructionType::IXOR_R:
asmCode << "xor " << regR[instr.dst] << ", " << regR[instr.src] << std::endl; asmCode << "xor " << regR[instr.dst] << ", " << regR[instr.src] << std::endl;
break; break;
case RandomX::SuperscalarInstructionType::IADD_RS: case SuperscalarInstructionType::IADD_RS:
asmCode << "lea " << regR[instr.dst] << ", [" << regR[instr.dst] << "+" << regR[instr.src] << "*" << (1 << (instr.getModShift2())) << "]" << std::endl; asmCode << "lea " << regR[instr.dst] << ", [" << regR[instr.dst] << "+" << regR[instr.src] << "*" << (1 << (instr.getModShift2())) << "]" << std::endl;
break; break;
case RandomX::SuperscalarInstructionType::IMUL_R: case SuperscalarInstructionType::IMUL_R:
asmCode << "imul " << regR[instr.dst] << ", " << regR[instr.src] << std::endl; asmCode << "imul " << regR[instr.dst] << ", " << regR[instr.src] << std::endl;
break; break;
case RandomX::SuperscalarInstructionType::IROR_C: case SuperscalarInstructionType::IROR_C:
asmCode << "ror " << regR[instr.dst] << ", " << instr.getImm32() << std::endl; asmCode << "ror " << regR[instr.dst] << ", " << instr.getImm32() << std::endl;
break; break;
case RandomX::SuperscalarInstructionType::IADD_C7: case SuperscalarInstructionType::IADD_C7:
asmCode << "add " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl; asmCode << "add " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl;
break; break;
case RandomX::SuperscalarInstructionType::IXOR_C7: case SuperscalarInstructionType::IXOR_C7:
asmCode << "xor " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl; asmCode << "xor " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl;
break; break;
case RandomX::SuperscalarInstructionType::IADD_C8: case SuperscalarInstructionType::IADD_C8:
asmCode << "add " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl; asmCode << "add " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl;
asmCode << "nop" << std::endl; asmCode << "nop" << std::endl;
break; break;
case RandomX::SuperscalarInstructionType::IXOR_C8: case SuperscalarInstructionType::IXOR_C8:
asmCode << "xor " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl; asmCode << "xor " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl;
asmCode << "nop" << std::endl; asmCode << "nop" << std::endl;
break; break;
case RandomX::SuperscalarInstructionType::IADD_C9: case SuperscalarInstructionType::IADD_C9:
asmCode << "add " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl; asmCode << "add " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl;
asmCode << "xchg ax, ax ;nop" << std::endl; asmCode << "xchg ax, ax ;nop" << std::endl;
break; break;
case RandomX::SuperscalarInstructionType::IXOR_C9: case SuperscalarInstructionType::IXOR_C9:
asmCode << "xor " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl; asmCode << "xor " << regR[instr.dst] << ", " << (int32_t)instr.getImm32() << std::endl;
asmCode << "xchg ax, ax ;nop" << std::endl; asmCode << "xchg ax, ax ;nop" << std::endl;
break; break;
case RandomX::SuperscalarInstructionType::IMULH_R: case SuperscalarInstructionType::IMULH_R:
asmCode << "mov rax, " << regR[instr.dst] << std::endl; asmCode << "mov rax, " << regR[instr.dst] << std::endl;
asmCode << "mul " << regR[instr.src] << std::endl; asmCode << "mul " << regR[instr.src] << std::endl;
asmCode << "mov " << regR[instr.dst] << ", rdx" << std::endl; asmCode << "mov " << regR[instr.dst] << ", rdx" << std::endl;
break; break;
case RandomX::SuperscalarInstructionType::ISMULH_R: case SuperscalarInstructionType::ISMULH_R:
asmCode << "mov rax, " << regR[instr.dst] << std::endl; asmCode << "mov rax, " << regR[instr.dst] << std::endl;
asmCode << "imul " << regR[instr.src] << std::endl; asmCode << "imul " << regR[instr.src] << std::endl;
asmCode << "mov " << regR[instr.dst] << ", rdx" << std::endl; asmCode << "mov " << regR[instr.dst] << ", rdx" << std::endl;
break; break;
case RandomX::SuperscalarInstructionType::IMUL_RCP: case SuperscalarInstructionType::IMUL_RCP:
asmCode << "mov rax, " << (int64_t)reciprocal(instr.getImm32()) << std::endl; asmCode << "mov rax, " << (int64_t)reciprocal(instr.getImm32()) << std::endl;
asmCode << "imul " << regR[instr.dst] << ", rax" << std::endl; asmCode << "imul " << regR[instr.dst] << ", rax" << std::endl;
break; break;
@ -178,38 +178,38 @@ namespace RandomX {
Instruction& instr = prog(i); Instruction& instr = prog(i);
switch (instr.opcode) switch (instr.opcode)
{ {
case RandomX::SuperscalarInstructionType::ISUB_R: case SuperscalarInstructionType::ISUB_R:
asmCode << regR[instr.dst] << " -= " << regR[instr.src] << ";" << std::endl; asmCode << regR[instr.dst] << " -= " << regR[instr.src] << ";" << std::endl;
break; break;
case RandomX::SuperscalarInstructionType::IXOR_R: case SuperscalarInstructionType::IXOR_R:
asmCode << regR[instr.dst] << " ^= " << regR[instr.src] << ";" << std::endl; asmCode << regR[instr.dst] << " ^= " << regR[instr.src] << ";" << std::endl;
break; break;
case RandomX::SuperscalarInstructionType::IADD_RS: case SuperscalarInstructionType::IADD_RS:
asmCode << regR[instr.dst] << " += " << regR[instr.src] << "*" << (1 << (instr.getModShift2())) << ";" << std::endl; asmCode << regR[instr.dst] << " += " << regR[instr.src] << "*" << (1 << (instr.getModShift2())) << ";" << std::endl;
break; break;
case RandomX::SuperscalarInstructionType::IMUL_R: case SuperscalarInstructionType::IMUL_R:
asmCode << regR[instr.dst] << " *= " << regR[instr.src] << ";" << std::endl; asmCode << regR[instr.dst] << " *= " << regR[instr.src] << ";" << std::endl;
break; break;
case RandomX::SuperscalarInstructionType::IROR_C: case SuperscalarInstructionType::IROR_C:
asmCode << regR[instr.dst] << " = rotr(" << regR[instr.dst] << ", " << instr.getImm32() << ");" << std::endl; asmCode << regR[instr.dst] << " = rotr(" << regR[instr.dst] << ", " << instr.getImm32() << ");" << std::endl;
break; break;
case RandomX::SuperscalarInstructionType::IADD_C7: case SuperscalarInstructionType::IADD_C7:
case RandomX::SuperscalarInstructionType::IADD_C8: case SuperscalarInstructionType::IADD_C8:
case RandomX::SuperscalarInstructionType::IADD_C9: case SuperscalarInstructionType::IADD_C9:
asmCode << regR[instr.dst] << " += " << (int32_t)instr.getImm32() << ";" << std::endl; asmCode << regR[instr.dst] << " += " << (int32_t)instr.getImm32() << ";" << std::endl;
break; break;
case RandomX::SuperscalarInstructionType::IXOR_C7: case SuperscalarInstructionType::IXOR_C7:
case RandomX::SuperscalarInstructionType::IXOR_C8: case SuperscalarInstructionType::IXOR_C8:
case RandomX::SuperscalarInstructionType::IXOR_C9: case SuperscalarInstructionType::IXOR_C9:
asmCode << regR[instr.dst] << " ^= " << (int32_t)instr.getImm32() << ";" << std::endl; asmCode << regR[instr.dst] << " ^= " << (int32_t)instr.getImm32() << ";" << std::endl;
break; break;
case RandomX::SuperscalarInstructionType::IMULH_R: case SuperscalarInstructionType::IMULH_R:
asmCode << regR[instr.dst] << " = mulh(" << regR[instr.dst] << ", " << regR[instr.src] << ");" << std::endl; asmCode << regR[instr.dst] << " = mulh(" << regR[instr.dst] << ", " << regR[instr.src] << ");" << std::endl;
break; break;
case RandomX::SuperscalarInstructionType::ISMULH_R: case SuperscalarInstructionType::ISMULH_R:
asmCode << regR[instr.dst] << " = smulh(" << regR[instr.dst] << ", " << regR[instr.src] << ");" << std::endl; asmCode << regR[instr.dst] << " = smulh(" << regR[instr.dst] << ", " << regR[instr.src] << ");" << std::endl;
break; break;
case RandomX::SuperscalarInstructionType::IMUL_RCP: case SuperscalarInstructionType::IMUL_RCP:
asmCode << regR[instr.dst] << " *= " << (int64_t)reciprocal(instr.getImm32()) << ";" << std::endl; asmCode << regR[instr.dst] << " *= " << (int64_t)reciprocal(instr.getImm32()) << ";" << std::endl;
break; break;
default: default:

View File

@ -24,7 +24,7 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
#include "common.hpp" #include "common.hpp"
#include <sstream> #include <sstream>
namespace RandomX { namespace randomx {
class Program; class Program;
class SuperscalarProgram; class SuperscalarProgram;

View File

@ -23,7 +23,7 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
#include "Blake2Generator.hpp" #include "Blake2Generator.hpp"
#include "common.hpp" #include "common.hpp"
namespace RandomX { namespace randomx {
Blake2Generator::Blake2Generator(const void* seed, int nonce) : dataIndex(sizeof(data)) { Blake2Generator::Blake2Generator(const void* seed, int nonce) : dataIndex(sizeof(data)) {
memset(data, 0, sizeof(data)); memset(data, 0, sizeof(data));

View File

@ -20,7 +20,7 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
#pragma once #pragma once
#include <cstdint> #include <cstdint>
namespace RandomX { namespace randomx {
class Blake2Generator { class Blake2Generator {
public: public:

View File

@ -1,82 +0,0 @@
/*
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/>.
*/
#include <cstring>
#include "Cache.hpp"
#include "argon2.h"
#include "argon2_core.h"
namespace RandomX {
static_assert(RANDOMX_ARGON_MEMORY % (RANDOMX_ARGON_LANES * ARGON2_SYNC_POINTS) == 0, "RANDOMX_ARGON_MEMORY - invalid value");
static_assert(RANDOMX_ARGON_GROWTH % (RANDOMX_ARGON_LANES * ARGON2_SYNC_POINTS) == 0, "RANDOMX_ARGON_GROWTH - invalid value");
void argonFill(Cache& cache, const void* seed, size_t seedSize) {
uint32_t memory_blocks, segment_length;
argon2_instance_t instance;
argon2_context context;
context.out = nullptr;
context.outlen = 0;
context.pwd = CONST_CAST(uint8_t *)seed;
context.pwdlen = (uint32_t)seedSize;
context.salt = CONST_CAST(uint8_t *)RANDOMX_ARGON_SALT;
context.saltlen = (uint32_t)ArgonSaltSize;
context.secret = NULL;
context.secretlen = 0;
context.ad = NULL;
context.adlen = 0;
context.t_cost = RANDOMX_ARGON_ITERATIONS;
context.m_cost = cache.size / ArgonBlockSize;
context.lanes = RANDOMX_ARGON_LANES;
context.threads = 1;
context.allocate_cbk = NULL;
context.free_cbk = NULL;
context.flags = ARGON2_DEFAULT_FLAGS;
context.version = ARGON2_VERSION_NUMBER;
/* 2. Align memory size */
/* Minimum memory_blocks = 8L blocks, where L is the number of lanes */
memory_blocks = context.m_cost;
segment_length = memory_blocks / (context.lanes * ARGON2_SYNC_POINTS);
instance.version = context.version;
instance.memory = NULL;
instance.passes = context.t_cost;
instance.memory_blocks = memory_blocks;
instance.segment_length = segment_length;
instance.lane_length = segment_length * ARGON2_SYNC_POINTS;
instance.lanes = context.lanes;
instance.threads = context.threads;
instance.type = Argon2_d;
instance.memory = (block*)cache.memory;
if (instance.threads > instance.lanes) {
instance.threads = instance.lanes;
}
/* 3. Initialization: Hashing inputs, allocating memory, filling first
* blocks
*/
argon_initialize(&instance, &context);
fill_memory_blocks(&instance);
}
}

View File

@ -1,52 +0,0 @@
/*
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/>.
*/
#pragma once
#include <cstdint>
#include <new>
#include "common.hpp"
#include "intrinPortable.h"
#include "virtualMemory.hpp"
namespace RandomX {
void argonFill(Cache& cache, const void* seed, size_t seedSize);
inline uint8_t* allocCache(size_t size, bool largePages) {
if (largePages) {
return (uint8_t*)allocLargePagesMemory(size);
}
else {
void* ptr = _mm_malloc(size, CacheLineSize);
if (ptr == nullptr)
throw std::bad_alloc();
return (uint8_t*)ptr;
}
}
inline void deallocCache(Cache cache, bool largePages) {
if (largePages) {
freePagedMemory(cache.memory, cache.size);
}
else {
_mm_free(cache.memory);
}
}
}

View File

@ -21,27 +21,25 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
#include "common.hpp" #include "common.hpp"
#include <stdexcept> #include <stdexcept>
namespace RandomX { namespace randomx {
template<bool superscalar> template<class Allocator, bool softAes>
void CompiledLightVirtualMachine<superscalar>::setDataset(dataset_t ds, uint64_t size, SuperscalarProgram(&programs)[RANDOMX_CACHE_ACCESSES]) { void CompiledLightVm<Allocator, softAes>::setCache(randomx_cache* cache) {
mem.ds = ds; this->mem.memory = cache->memory;
datasetRange = (size - RANDOMX_DATASET_SIZE + CacheLineSize) / CacheLineSize; //datasetRange = (size - RANDOMX_DATASET_SIZE + CacheLineSize) / CacheLineSize;
if(superscalar) this->compiler.generateSuperscalarHash(cache->programs, cache->reciprocalCache);
compiler.generateSuperScalarHash(programs);
//datasetBasePtr = ds.dataset.memory; //datasetBasePtr = ds.dataset.memory;
} }
template void CompiledLightVirtualMachine<true>::setDataset(dataset_t ds, uint64_t size, SuperscalarProgram(&programs)[RANDOMX_CACHE_ACCESSES]); template<class Allocator, bool softAes>
template void CompiledLightVirtualMachine<false>::setDataset(dataset_t ds, uint64_t size, SuperscalarProgram(&programs)[RANDOMX_CACHE_ACCESSES]); void CompiledLightVm<Allocator, softAes>::initialize() {
randomx_vm::initialize();
template<bool superscalar> this->compiler.generateProgramLight(this->program, this->config);
void CompiledLightVirtualMachine<superscalar>::initialize() {
VirtualMachine::initialize();
compiler.generateProgramLight<superscalar>(program, config);
//mem.ds.dataset.memory = datasetBasePtr + (datasetBase * CacheLineSize); //mem.ds.dataset.memory = datasetBasePtr + (datasetBase * CacheLineSize);
} }
template void CompiledLightVirtualMachine<true>::initialize(); template class CompiledLightVm<AlignedAllocator<CacheLineSize>, false>;
template void CompiledLightVirtualMachine<false>::initialize(); template class CompiledLightVm<AlignedAllocator<CacheLineSize>, true>;
template class CompiledLightVm<LargePageAllocator, false>;
template class CompiledLightVm<LargePageAllocator, true>;
} }

View File

@ -24,22 +24,27 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
#include "JitCompilerX86.hpp" #include "JitCompilerX86.hpp"
#include "intrinPortable.h" #include "intrinPortable.h"
namespace RandomX { namespace randomx {
template<bool superscalar> template<class Allocator, bool softAes>
class CompiledLightVirtualMachine : public CompiledVirtualMachine { class CompiledLightVm : public CompiledVm<Allocator, softAes> {
public: public:
void* operator new(size_t size) { void* operator new(size_t size) {
void* ptr = _mm_malloc(size, 64); void* ptr = AlignedAllocator<CacheLineSize>::allocMemory(size);
if (ptr == nullptr) if (ptr == nullptr)
throw std::bad_alloc(); throw std::bad_alloc();
return ptr; return ptr;
} }
void operator delete(void* ptr) { void operator delete(void* ptr) {
_mm_free(ptr); AlignedAllocator<CacheLineSize>::freeMemory(ptr, sizeof(CompiledLightVm));
} }
CompiledLightVirtualMachine() {} void setCache(randomx_cache* cache) override;
void setDataset(dataset_t ds, uint64_t size, SuperscalarProgram(&programs)[RANDOMX_CACHE_ACCESSES]) override; void setDataset(randomx_dataset* dataset) override {}
void initialize() override; void initialize() override;
}; };
using CompiledLightVmDefault = CompiledLightVm<AlignedAllocator<CacheLineSize>, true>;
using CompiledLightVmHardAes = CompiledLightVm<AlignedAllocator<CacheLineSize>, false>;
using CompiledLightVmLargePage = CompiledLightVm<LargePageAllocator, false>;
using CompiledLightVmLargePageHardAes = CompiledLightVm<LargePageAllocator, true>;
} }

View File

@ -21,34 +21,34 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
#include "common.hpp" #include "common.hpp"
#include <stdexcept> #include <stdexcept>
namespace RandomX { namespace randomx {
//static_assert(sizeof(MemoryRegisters) == 2 * sizeof(addr_t) + sizeof(uintptr_t), "Invalid alignment of struct RandomX::MemoryRegisters"); static_assert(sizeof(MemoryRegisters) == 2 * sizeof(addr_t) + sizeof(uintptr_t), "Invalid alignment of struct randomx::MemoryRegisters");
static_assert(sizeof(RegisterFile) == 256, "Invalid alignment of struct RandomX::RegisterFile"); static_assert(sizeof(RegisterFile) == 256, "Invalid alignment of struct randomx::RegisterFile");
CompiledVirtualMachine::CompiledVirtualMachine() {
template<class Allocator, bool softAes>
void CompiledVm<Allocator, softAes>::setDataset(randomx_dataset* dataset) {
this->mem.memory = dataset->memory;
//datasetRange = (size - RANDOMX_DATASET_SIZE + CacheLineSize) / CacheLineSize;
//datasetBasePtr = ds.dataset.memory;
} }
void CompiledVirtualMachine::setDataset(dataset_t ds, uint64_t size, SuperscalarProgram(&programs)[RANDOMX_CACHE_ACCESSES]) { template<class Allocator, bool softAes>
mem.ds = ds; void CompiledVm<Allocator, softAes>::initialize() {
datasetRange = (size - RANDOMX_DATASET_SIZE + CacheLineSize) / CacheLineSize; randomx_vm::initialize();
datasetBasePtr = ds.dataset.memory; this->compiler.generateProgram(this->program, this->config);
//mem.ds.dataset.memory = datasetBasePtr + (datasetBase * CacheLineSize);
} }
void CompiledVirtualMachine::initialize() { template<class Allocator, bool softAes>
VirtualMachine::initialize(); void CompiledVm<Allocator, softAes>::execute() {
compiler.generateProgram(program, config);
mem.ds.dataset.memory = datasetBasePtr + (datasetBase * CacheLineSize);
}
void CompiledVirtualMachine::execute() {
//executeProgram(reg, mem, scratchpad, InstructionCount); //executeProgram(reg, mem, scratchpad, InstructionCount);
compiler.getProgramFunc()(reg, mem, scratchpad, RANDOMX_PROGRAM_ITERATIONS); compiler.getProgramFunc()(this->reg, this->mem, this->scratchpad, RANDOMX_PROGRAM_ITERATIONS);
#ifdef TRACEVM
for (int32_t i = InstructionCount - 1; i >= 0; --i) {
std::cout << std::hex << tracepad[i].u64 << std::endl;
} }
#endif
} template class CompiledVm<AlignedAllocator<CacheLineSize>, false>;
template class CompiledVm<AlignedAllocator<CacheLineSize>, true>;
template class CompiledVm<LargePageAllocator, false>;
template class CompiledVm<LargePageAllocator, true>;
} }

View File

@ -18,38 +18,39 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
//#define TRACEVM
#include <new> #include <new>
#include "VirtualMachine.hpp" #include "VirtualMachine.hpp"
#include "JitCompilerX86.hpp" #include "JitCompilerX86.hpp"
#include "intrinPortable.h"
namespace RandomX { namespace randomx {
extern "C" { extern "C" {
void executeProgram(RegisterFile&, MemoryRegisters&, uint8_t* /* scratchpad */, uint64_t); void executeProgram(RegisterFile&, MemoryRegisters&, uint8_t* /* scratchpad */, uint64_t);
} }
class CompiledVirtualMachine : public VirtualMachine { template<class Allocator, bool softAes>
class CompiledVm : public VmBase<Allocator, softAes> {
public: public:
void* operator new(size_t size) { void* operator new(size_t size) {
void* ptr = _mm_malloc(size, 64); void* ptr = AlignedAllocator<CacheLineSize>::allocMemory(size);
if (ptr == nullptr) if (ptr == nullptr)
throw std::bad_alloc(); throw std::bad_alloc();
return ptr; return ptr;
} }
void operator delete(void* ptr) { void operator delete(void* ptr) {
_mm_free(ptr); AlignedAllocator<CacheLineSize>::freeMemory(ptr, sizeof(CompiledVm));
} }
CompiledVirtualMachine(); void setDataset(randomx_dataset* dataset) override;
void setDataset(dataset_t ds, uint64_t size, SuperscalarProgram(&programs)[RANDOMX_CACHE_ACCESSES]) override; void execute() override;
void initialize() override; void initialize() override;
virtual void execute() override;
void* getProgram() {
return compiler.getCode();
}
protected: protected:
JitCompilerX86 compiler; JitCompilerX86 compiler;
uint8_t* datasetBasePtr; uint8_t* datasetBasePtr;
}; };
using CompiledVmDefault = CompiledVm<AlignedAllocator<CacheLineSize>, true>;
using CompiledVmHardAes = CompiledVm<AlignedAllocator<CacheLineSize>, false>;
using CompiledVmLargePage = CompiledVm<LargePageAllocator, false>;
using CompiledVmLargePageHardAes = CompiledVm<LargePageAllocator, true>;
} }

View File

@ -20,7 +20,7 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
#include "Instruction.hpp" #include "Instruction.hpp"
#include "common.hpp" #include "common.hpp"
namespace RandomX { namespace randomx {
void Instruction::print(std::ostream& os) const { void Instruction::print(std::ostream& os) const {
os << names[opcode] << " "; os << names[opcode] << " ";

View File

@ -23,7 +23,7 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
#include <iostream> #include <iostream>
#include "blake2/endian.h" #include "blake2/endian.h"
namespace RandomX { namespace randomx {
class Instruction; class Instruction;

View File

@ -0,0 +1,47 @@
/*
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/>.
*/
#include "InterpretedLightVirtualMachine.hpp"
#include "dataset.hpp"
namespace randomx {
template<class Allocator, bool softAes>
void InterpretedLightVm<Allocator, softAes>::setCache(randomx_cache* cache) {
mem.memory = cache->memory;
//datasetRange = (size - RANDOMX_DATASET_SIZE + CacheLineSize) / CacheLineSize;
cachePtr = cache;
}
template<class Allocator, bool softAes>
void InterpretedLightVm<Allocator, softAes>::datasetRead(uint32_t address, int_reg_t(&r)[8]) {
uint32_t blockNumber = address / CacheLineSize;
int_reg_t rl[8];
initDatasetBlock(cachePtr, (uint8_t*)rl, blockNumber);
for (unsigned q = 0; q < 8; ++q)
r[q] ^= rl[q];
}
template class InterpretedLightVm<AlignedAllocator<CacheLineSize>, false>;
template class InterpretedLightVm<AlignedAllocator<CacheLineSize>, true>;
template class InterpretedLightVm<LargePageAllocator, false>;
template class InterpretedLightVm<LargePageAllocator, true>;
}

View File

@ -0,0 +1,53 @@
/*
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/>.
*/
#pragma once
#include <new>
#include "InterpretedVirtualMachine.hpp"
#include "superscalar_program.hpp"
namespace randomx {
template<class Allocator, bool softAes>
class InterpretedLightVm : public InterpretedVm<Allocator, softAes> {
public:
using VmBase<Allocator, softAes>::mem;
void* operator new(size_t size) {
void* ptr = AlignedAllocator<CacheLineSize>::allocMemory(size);
if (ptr == nullptr)
throw std::bad_alloc();
return ptr;
}
void operator delete(void* ptr) {
AlignedAllocator<CacheLineSize>::freeMemory(ptr, sizeof(InterpretedLightVm));
}
void setDataset(randomx_dataset* dataset) override { }
void setCache(randomx_cache* cache) override;
protected:
virtual void datasetRead(uint32_t address, int_reg_t(&r)[8]);
private:
randomx_cache* cachePtr;
};
using InterpretedLightVmDefault = InterpretedLightVm<AlignedAllocator<CacheLineSize>, true>;
using InterpretedLightVmHardAes = InterpretedLightVm<AlignedAllocator<CacheLineSize>, false>;
using InterpretedLightVmLargePage = InterpretedLightVm<LargePageAllocator, false>;
using InterpretedLightVmLargePageHardAes = InterpretedLightVm<LargePageAllocator, true>;
}

View File

@ -21,7 +21,6 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
#define RANDOMX_JUMP #define RANDOMX_JUMP
#include "InterpretedVirtualMachine.hpp" #include "InterpretedVirtualMachine.hpp"
#include "dataset.hpp" #include "dataset.hpp"
#include "Cache.hpp"
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
#include <stdexcept> #include <stdexcept>
@ -32,10 +31,6 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
#include <climits> #include <climits>
#include "intrinPortable.h" #include "intrinPortable.h"
#include "reciprocal.h" #include "reciprocal.h"
#ifdef STATS
#include <algorithm>
#endif
#include "superscalarGenerator.hpp"
#ifdef FPUCHECK #ifdef FPUCHECK
constexpr bool fpuCheck = true; constexpr bool fpuCheck = true;
@ -43,44 +38,31 @@ constexpr bool fpuCheck = true;
constexpr bool fpuCheck = false; constexpr bool fpuCheck = false;
#endif #endif
namespace RandomX { namespace randomx {
static int_reg_t Zero = 0; static int_reg_t Zero = 0;
template<bool superscalar> template<class Allocator, bool softAes>
void InterpretedVirtualMachine<superscalar>::setDataset(dataset_t ds, uint64_t size, SuperscalarProgram(&programs)[RANDOMX_CACHE_ACCESSES]) { void InterpretedVm<Allocator, softAes>::setDataset(randomx_dataset* dataset) {
mem.ds = ds; mem.memory = dataset->memory;
readDataset = &datasetReadLight;
datasetRange = (size - RANDOMX_DATASET_SIZE + CacheLineSize) / CacheLineSize;
if(superscalar)
precompileSuperscalar(programs);
} }
template void InterpretedVirtualMachine<true>::setDataset(dataset_t ds, uint64_t size, SuperscalarProgram(&programs)[RANDOMX_CACHE_ACCESSES]); template<class Allocator, bool softAes>
template void InterpretedVirtualMachine<false>::setDataset(dataset_t ds, uint64_t size, SuperscalarProgram(&programs)[RANDOMX_CACHE_ACCESSES]); void InterpretedVm<Allocator, softAes>::initialize() {
randomx_vm::initialize();
template<bool superscalar>
void InterpretedVirtualMachine<superscalar>::initialize() {
VirtualMachine::initialize();
for (unsigned i = 0; i < RANDOMX_PROGRAM_SIZE; ++i) { for (unsigned i = 0; i < RANDOMX_PROGRAM_SIZE; ++i) {
program(i).src %= RegistersCount; program(i).src %= RegistersCount;
program(i).dst %= RegistersCount; program(i).dst %= RegistersCount;
} }
} }
template void InterpretedVirtualMachine<true>::initialize(); template<class Allocator, bool softAes>
template void InterpretedVirtualMachine<false>::initialize(); void InterpretedVm<Allocator, softAes>::executeBytecode(int_reg_t(&r)[8], __m128d (&f)[4], __m128d (&e)[4], __m128d (&a)[4]) {
template<bool superscalar>
void InterpretedVirtualMachine<superscalar>::executeBytecode(int_reg_t(&r)[8], __m128d (&f)[4], __m128d (&e)[4], __m128d (&a)[4]) {
for (int ic = 0; ic < RANDOMX_PROGRAM_SIZE; ++ic) { for (int ic = 0; ic < RANDOMX_PROGRAM_SIZE; ++ic) {
executeBytecode(ic, r, f, e, a); executeBytecode(ic, r, f, e, a);
} }
} }
template void InterpretedVirtualMachine<true>::executeBytecode(int_reg_t(&r)[8], __m128d (&f)[4], __m128d (&e)[4], __m128d (&a)[4]);
template void InterpretedVirtualMachine<false>::executeBytecode(int_reg_t(&r)[8], __m128d (&f)[4], __m128d (&e)[4], __m128d (&a)[4]);
static void print(int_reg_t r) { static void print(int_reg_t r) {
std::cout << std::hex << std::setw(16) << std::setfill('0') << r << std::endl; std::cout << std::hex << std::setw(16) << std::setfill('0') << r << std::endl;
} }
@ -110,14 +92,14 @@ namespace RandomX {
return std::fpclassify(x) == FP_SUBNORMAL; return std::fpclassify(x) == FP_SUBNORMAL;
} }
template<bool superscalar> template<class Allocator, bool softAes>
FORCE_INLINE void* InterpretedVirtualMachine<superscalar>::getScratchpadAddress(InstructionByteCode& ibc) { FORCE_INLINE void* InterpretedVm<Allocator, softAes>::getScratchpadAddress(InstructionByteCode& ibc) {
uint32_t addr = (*ibc.isrc + ibc.imm) & ibc.memMask; uint32_t addr = (*ibc.isrc + ibc.imm) & ibc.memMask;
return scratchpad + addr; return scratchpad + addr;
} }
template<bool superscalar> template<class Allocator, bool softAes>
FORCE_INLINE __m128d InterpretedVirtualMachine<superscalar>::maskRegisterExponentMantissa(__m128d x) { FORCE_INLINE __m128d InterpretedVm<Allocator, softAes>::maskRegisterExponentMantissa(__m128d x) {
constexpr uint64_t mantissaMask64 = (1ULL << 52) - 1; constexpr uint64_t mantissaMask64 = (1ULL << 52) - 1;
const __m128d mantissaMask = _mm_castsi128_pd(_mm_set_epi64x(mantissaMask64, mantissaMask64)); const __m128d mantissaMask = _mm_castsi128_pd(_mm_set_epi64x(mantissaMask64, mantissaMask64));
const __m128d exponentMask = _mm_load_pd((const double*)&config.eMask); const __m128d exponentMask = _mm_load_pd((const double*)&config.eMask);
@ -126,8 +108,8 @@ namespace RandomX {
return x; return x;
} }
template<bool superscalar> template<class Allocator, bool softAes>
FORCE_INLINE void InterpretedVirtualMachine<superscalar>::executeBytecode(int& ic, int_reg_t(&r)[8], __m128d (&f)[4], __m128d (&e)[4], __m128d (&a)[4]) { void InterpretedVm<Allocator, softAes>::executeBytecode(int& ic, int_reg_t(&r)[8], __m128d (&f)[4], __m128d (&e)[4], __m128d (&a)[4]) {
auto& ibc = byteCode[ic]; auto& ibc = byteCode[ic];
if (trace) std::cout << std::dec << std::setw(3) << ic << " " << program(ic); if (trace) std::cout << std::dec << std::setw(3) << ic << " " << program(ic);
//if(trace) printState(r, f, e, a); //if(trace) printState(r, f, e, a);
@ -318,8 +300,8 @@ namespace RandomX {
#endif #endif
} }
template<bool superscalar> template<class Allocator, bool softAes>
void InterpretedVirtualMachine<superscalar>::execute() { void InterpretedVm<Allocator, softAes>::execute() {
int_reg_t r[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; int_reg_t r[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
__m128d f[4]; __m128d f[4];
__m128d e[4]; __m128d e[4];
@ -380,16 +362,8 @@ namespace RandomX {
mem.mx ^= r[config.readReg2] ^ r[config.readReg3]; mem.mx ^= r[config.readReg2] ^ r[config.readReg3];
mem.mx &= CacheLineAlignMask; mem.mx &= CacheLineAlignMask;
if (superscalar) { datasetRead(mem.ma, r);
executeSuperscalar(datasetBase + mem.ma / CacheLineSize, r); //executeSuperscalar(datasetBase + mem.ma / CacheLineSize, r);
}
else {
Cache& cache = mem.ds.cache;
uint64_t datasetLine[CacheLineSize / sizeof(uint64_t)];
initBlock(cache, (uint8_t*)datasetLine, datasetBase + mem.ma / CacheLineSize, RANDOMX_CACHE_ACCESSES / 8);
for (int i = 0; i < RegistersCount; ++i)
r[i] ^= datasetLine[i];
}
std::swap(mem.mx, mem.ma); std::swap(mem.mx, mem.ma);
if (trace) { if (trace) {
@ -454,9 +428,6 @@ namespace RandomX {
_mm_store_pd(&reg.e[3].lo, e[3]); _mm_store_pd(&reg.e[3].lo, e[3]);
} }
template void InterpretedVirtualMachine<true>::execute();
template void InterpretedVirtualMachine<false>::execute();
static int getConditionRegister(int(&registerUsage)[8]) { static int getConditionRegister(int(&registerUsage)[8]) {
int min = INT_MAX; int min = INT_MAX;
int minIndex; int minIndex;
@ -469,108 +440,14 @@ namespace RandomX {
return minIndex; return minIndex;
} }
constexpr uint64_t superscalarMul0 = 6364136223846793005ULL; template<class Allocator, bool softAes>
constexpr uint64_t superscalarAdd1 = 9298410992540426748ULL; void InterpretedVm<Allocator, softAes>::datasetRead(uint32_t address, int_reg_t(&r)[8]) {
constexpr uint64_t superscalarAdd2 = 12065312585734608966ULL; uint64_t* datasetLine = (uint64_t*)(mem.memory + address);
constexpr uint64_t superscalarAdd3 = 9306329213124610396ULL; for (int i = 0; i < RegistersCount; ++i)
constexpr uint64_t superscalarAdd4 = 5281919268842080866ULL; r[i] ^= datasetLine[i];
constexpr uint64_t superscalarAdd5 = 10536153434571861004ULL;
constexpr uint64_t superscalarAdd6 = 3398623926847679864ULL;
constexpr uint64_t superscalarAdd7 = 9549104520008361294ULL;
static uint8_t* getMixBlock(uint64_t registerValue, Cache& cache) {
uint8_t* mixBlock;
if (RANDOMX_ARGON_GROWTH == 0) {
constexpr uint32_t mask = (RANDOMX_ARGON_MEMORY * ArgonBlockSize / CacheLineSize - 1);
mixBlock = cache.memory + (registerValue & mask) * CacheLineSize;
}
else {
const uint32_t modulus = cache.size / CacheLineSize;
mixBlock = cache.memory + (registerValue % modulus) * CacheLineSize;
}
return mixBlock;
} }
template<bool superscalar> /*template<bool superscalar>
void InterpretedVirtualMachine<superscalar>::executeSuperscalar(int_reg_t(&r)[8], SuperscalarProgram& prog, std::vector<uint64_t>& reciprocals) {
for (unsigned j = 0; j < prog.getSize(); ++j) {
Instruction& instr = prog(j);
switch (instr.opcode)
{
case RandomX::SuperscalarInstructionType::ISUB_R:
r[instr.dst] -= r[instr.src];
break;
case RandomX::SuperscalarInstructionType::IXOR_R:
r[instr.dst] ^= r[instr.src];
break;
case RandomX::SuperscalarInstructionType::IADD_RS:
r[instr.dst] += r[instr.src] << instr.getModShift2();
break;
case RandomX::SuperscalarInstructionType::IMUL_R:
r[instr.dst] *= r[instr.src];
break;
case RandomX::SuperscalarInstructionType::IROR_C:
r[instr.dst] = rotr(r[instr.dst], instr.getImm32());
break;
case RandomX::SuperscalarInstructionType::IADD_C7:
case RandomX::SuperscalarInstructionType::IADD_C8:
case RandomX::SuperscalarInstructionType::IADD_C9:
r[instr.dst] += signExtend2sCompl(instr.getImm32());
break;
case RandomX::SuperscalarInstructionType::IXOR_C7:
case RandomX::SuperscalarInstructionType::IXOR_C8:
case RandomX::SuperscalarInstructionType::IXOR_C9:
r[instr.dst] ^= signExtend2sCompl(instr.getImm32());
break;
case RandomX::SuperscalarInstructionType::IMULH_R:
r[instr.dst] = mulh(r[instr.dst], r[instr.src]);
break;
case RandomX::SuperscalarInstructionType::ISMULH_R:
r[instr.dst] = smulh(r[instr.dst], r[instr.src]);
break;
case RandomX::SuperscalarInstructionType::IMUL_RCP:
if(superscalar)
r[instr.dst] *= reciprocals[instr.getImm32()];
else
r[instr.dst] *= reciprocal(instr.getImm32());
break;
default:
UNREACHABLE;
}
}
}
template<bool superscalar>
void InterpretedVirtualMachine<superscalar>::executeSuperscalar(uint32_t blockNumber, int_reg_t(&r)[8]) {
int_reg_t rl[8];
uint8_t* mixBlock;
uint64_t registerValue = blockNumber;
rl[0] = (blockNumber + 1) * superscalarMul0;
rl[1] = rl[0] ^ superscalarAdd1;
rl[2] = rl[0] ^ superscalarAdd2;
rl[3] = rl[0] ^ superscalarAdd3;
rl[4] = rl[0] ^ superscalarAdd4;
rl[5] = rl[0] ^ superscalarAdd5;
rl[6] = rl[0] ^ superscalarAdd6;
rl[7] = rl[0] ^ superscalarAdd7;
Cache& cache = mem.ds.cache;
for (unsigned i = 0; i < RANDOMX_CACHE_ACCESSES; ++i) {
mixBlock = getMixBlock(registerValue, cache);
SuperscalarProgram& prog = superScalarPrograms[i];
executeSuperscalar(rl, prog, reciprocals);
for(unsigned q = 0; q < 8; ++q)
rl[q] ^= load64(mixBlock + 8 * q);
registerValue = rl[prog.getAddressRegister()];
}
for (unsigned q = 0; q < 8; ++q)
r[q] ^= rl[q];
}
template<bool superscalar>
void InterpretedVirtualMachine<superscalar>::precompileSuperscalar(SuperscalarProgram* programs) { void InterpretedVirtualMachine<superscalar>::precompileSuperscalar(SuperscalarProgram* programs) {
memcpy(superScalarPrograms, programs, sizeof(superScalarPrograms)); memcpy(superScalarPrograms, programs, sizeof(superScalarPrograms));
reciprocals.clear(); reciprocals.clear();
@ -584,12 +461,12 @@ namespace RandomX {
} }
} }
} }
} }*/
#include "instructionWeights.hpp" #include "instructionWeights.hpp"
template<bool superscalar> template<class Allocator, bool softAes>
void InterpretedVirtualMachine<superscalar>::precompileProgram(int_reg_t(&r)[8], __m128d (&f)[4], __m128d (&e)[4], __m128d (&a)[4]) { void InterpretedVm<Allocator, softAes>::precompileProgram(int_reg_t(&r)[8], __m128d (&f)[4], __m128d (&e)[4], __m128d (&a)[4]) {
int registerUsage[8]; int registerUsage[8];
for (unsigned i = 0; i < 8; ++i) { for (unsigned i = 0; i < 8; ++i) {
registerUsage[i] = -1; registerUsage[i] = -1;
@ -1007,4 +884,9 @@ namespace RandomX {
} }
} }
} }
template class InterpretedVm<AlignedAllocator<CacheLineSize>, false>;
template class InterpretedVm<AlignedAllocator<CacheLineSize>, true>;
template class InterpretedVm<LargePageAllocator, false>;
template class InterpretedVm<LargePageAllocator, true>;
} }

View File

@ -18,20 +18,14 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
//#define STATS
#include <new> #include <new>
#include "VirtualMachine.hpp" #include "VirtualMachine.hpp"
#include "Program.hpp" #include "Program.hpp"
#include "intrinPortable.h" #include "intrinPortable.h"
#include <vector> #include <vector>
namespace RandomX { namespace randomx {
struct InstructionByteCode;
template<bool superscalar> class InterpretedVirtualMachine;
template<bool superscalar>
using InstructionHandler = void(InterpretedVirtualMachine<superscalar>::*)(Instruction&);
struct InstructionByteCode { struct InstructionByteCode {
union { union {
@ -56,83 +50,40 @@ namespace RandomX {
constexpr int asedwfagdewsa = sizeof(InstructionByteCode); constexpr int asedwfagdewsa = sizeof(InstructionByteCode);
template<bool superscalar> template<class Allocator, bool softAes>
class InterpretedVirtualMachine : public VirtualMachine { class InterpretedVm : public VmBase<Allocator, softAes> {
public: public:
using VmBase<Allocator, softAes>::mem;
using VmBase<Allocator, softAes>::scratchpad;
using VmBase<Allocator, softAes>::program;
using VmBase<Allocator, softAes>::config;
using VmBase<Allocator, softAes>::reg;
void* operator new(size_t size) { void* operator new(size_t size) {
void* ptr = _mm_malloc(size, 64); void* ptr = AlignedAllocator<CacheLineSize>::allocMemory(size);
if (ptr == nullptr) if (ptr == nullptr)
throw std::bad_alloc(); throw std::bad_alloc();
return ptr; return ptr;
} }
void operator delete(void* ptr) { void operator delete(void* ptr) {
_mm_free(ptr); AlignedAllocator<CacheLineSize>::freeMemory(ptr, sizeof(InterpretedVm));
} }
InterpretedVirtualMachine(bool soft) : softAes(soft) {}
~InterpretedVirtualMachine() {}
void setDataset(dataset_t ds, uint64_t size, SuperscalarProgram(&programs)[RANDOMX_CACHE_ACCESSES]) override;
void initialize() override;
void execute() override; void execute() override;
static void executeSuperscalar(int_reg_t(&r)[8], SuperscalarProgram& prog, std::vector<uint64_t>& reciprocals); void setDataset(randomx_dataset* dataset) override;
void initialize() override;
protected:
virtual void datasetRead(uint32_t blockNumber, int_reg_t(&r)[8]);
private: private:
static InstructionHandler<superscalar> engine[256];
DatasetReadFunc readDataset;
bool softAes;
InstructionByteCode byteCode[RANDOMX_PROGRAM_SIZE];
std::vector<uint64_t> reciprocals;
alignas(64) SuperscalarProgram superScalarPrograms[RANDOMX_CACHE_ACCESSES];
#ifdef STATS
int count_ADD_64 = 0;
int count_ADD_32 = 0;
int count_SUB_64 = 0;
int count_SUB_32 = 0;
int count_MUL_64 = 0;
int count_MULH_64 = 0;
int count_MUL_32 = 0;
int count_IMUL_32 = 0;
int count_IMULH_64 = 0;
int count_DIV_64 = 0;
int count_IDIV_64 = 0;
int count_AND_64 = 0;
int count_AND_32 = 0;
int count_OR_64 = 0;
int count_OR_32 = 0;
int count_XOR_64 = 0;
int count_XOR_32 = 0;
int count_SHL_64 = 0;
int count_SHR_64 = 0;
int count_SAR_64 = 0;
int count_ROL_64 = 0;
int count_ROR_64 = 0;
int count_FADD = 0;
int count_FSUB = 0;
int count_FMUL = 0;
int count_FDIV = 0;
int count_FSQRT = 0;
int count_FPROUND = 0;
int count_JUMP_taken = 0;
int count_JUMP_not_taken = 0;
int count_jump_taken[8] = { 0 };
int count_jump_not_taken[8] = { 0 };
int count_max_stack = 0;
int count_retdepth = 0;
int count_retdepth_max = 0;
int count_endstack = 0;
int count_instructions[RANDOMX_PROGRAM_SIZE] = { 0 };
int count_FADD_nop = 0;
int count_FADD_nop2 = 0;
int count_FSUB_nop = 0;
int count_FSUB_nop2 = 0;
int count_FMUL_nop = 0;
int count_FMUL_nop2 = 0;
int datasetAccess[256] = { 0 };
#endif
void precompileProgram(int_reg_t(&r)[8], __m128d (&f)[4], __m128d (&e)[4], __m128d (&a)[4]); void precompileProgram(int_reg_t(&r)[8], __m128d (&f)[4], __m128d (&e)[4], __m128d (&a)[4]);
void precompileSuperscalar(SuperscalarProgram*);
void executeBytecode(int_reg_t(&r)[8], __m128d (&f)[4], __m128d (&e)[4], __m128d (&a)[4]); void executeBytecode(int_reg_t(&r)[8], __m128d (&f)[4], __m128d (&e)[4], __m128d (&a)[4]);
void executeBytecode(int& i, int_reg_t(&r)[8], __m128d (&f)[4], __m128d (&e)[4], __m128d (&a)[4]); void executeBytecode(int& i, int_reg_t(&r)[8], __m128d (&f)[4], __m128d (&e)[4], __m128d (&a)[4]);
void executeSuperscalar(uint32_t blockNumber, int_reg_t(&r)[8]);
void* getScratchpadAddress(InstructionByteCode& ibc); void* getScratchpadAddress(InstructionByteCode& ibc);
__m128d maskRegisterExponentMantissa(__m128d); __m128d maskRegisterExponentMantissa(__m128d);
InstructionByteCode byteCode[RANDOMX_PROGRAM_SIZE];
}; };
using InterpretedVmDefault = InterpretedVm<AlignedAllocator<CacheLineSize>, true>;
using InterpretedVmHardAes = InterpretedVm<AlignedAllocator<CacheLineSize>, false>;
using InterpretedVmLargePage = InterpretedVm<LargePageAllocator, false>;
using InterpretedVmLargePageHardAes = InterpretedVm<LargePageAllocator, true>;
} }

View File

@ -97,7 +97,7 @@ DECL(randomx_dataset_init):
push r13 push r13
push r14 push r14
push r15 push r15
;# cache in rdi mov rdi, qword ptr [rdi+8] ;# after virtual method table pointer
;# dataset in rsi ;# dataset in rsi
mov rbp, rdx ;# block index mov rbp, rdx ;# block index
push rcx ;# max. block index push rcx ;# max. block index

View File

@ -100,7 +100,7 @@ randomx_dataset_init PROC
push r13 push r13
push r14 push r14
push r15 push r15
mov rdi, rcx ;# cache mov rdi, qword ptr [rcx+8] ;# after virtual method table pointer
mov rsi, rdx ;# dataset mov rsi, rdx ;# dataset
mov rbp, r8 ;# block index mov rbp, r8 ;# block index
push r9 ;# max. block index push r9 ;# max. block index

View File

@ -28,7 +28,7 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
#define RANDOMX_JUMP #define RANDOMX_JUMP
namespace RandomX { namespace randomx {
#if !defined(_M_X64) && !defined(__x86_64__) #if !defined(_M_X64) && !defined(__x86_64__)
JitCompilerX86::JitCompilerX86() { JitCompilerX86::JitCompilerX86() {
@ -238,33 +238,29 @@ namespace RandomX {
generateProgramEpilogue(prog); generateProgramEpilogue(prog);
} }
template<bool superscalar>
void JitCompilerX86::generateProgramLight(Program& prog, ProgramConfiguration& pcfg) { void JitCompilerX86::generateProgramLight(Program& prog, ProgramConfiguration& pcfg) {
if (RANDOMX_CACHE_ACCESSES != 8) if (RANDOMX_CACHE_ACCESSES != 8)
throw std::runtime_error("JIT compiler: Unsupported value of RANDOMX_CACHE_ACCESSES"); throw std::runtime_error("JIT compiler: Unsupported value of RANDOMX_CACHE_ACCESSES");
if (RANDOMX_ARGON_GROWTH != 0) if (RANDOMX_ARGON_GROWTH != 0)
throw std::runtime_error("JIT compiler: Unsupported value of RANDOMX_ARGON_GROWTH"); throw std::runtime_error("JIT compiler: Unsupported value of RANDOMX_ARGON_GROWTH");
generateProgramPrologue(prog, pcfg); generateProgramPrologue(prog, pcfg);
if (superscalar) { //if (superscalar) {
emit(codeReadDatasetLightSshInit, readDatasetLightInitSize); emit(codeReadDatasetLightSshInit, readDatasetLightInitSize);
emitByte(CALL); emitByte(CALL);
emit32(superScalarHashOffset - (codePos + 4)); emit32(superScalarHashOffset - (codePos + 4));
emit(codeReadDatasetLightSshFin, readDatasetLightFinSize); emit(codeReadDatasetLightSshFin, readDatasetLightFinSize);
} /*}
else { else {
memcpy(code + codePos, codeReadDatasetLight, readDatasetLightSize); memcpy(code + codePos, codeReadDatasetLight, readDatasetLightSize);
codePos += readDatasetLightSize; codePos += readDatasetLightSize;
emitByte(CALL); emitByte(CALL);
emit32(readDatasetLightSubOffset - (codePos + 4)); emit32(readDatasetLightSubOffset - (codePos + 4));
} }*/
generateProgramEpilogue(prog); generateProgramEpilogue(prog);
} }
template void JitCompilerX86::generateProgramLight<true>(Program& prog, ProgramConfiguration& pcfg);
template void JitCompilerX86::generateProgramLight<false>(Program& prog, ProgramConfiguration& pcfg);
template<size_t N> template<size_t N>
void JitCompilerX86::generateSuperScalarHash(SuperscalarProgram(&programs)[N]) { void JitCompilerX86::generateSuperscalarHash(SuperscalarProgram(&programs)[N], std::vector<uint64_t> &reciprocalCache) {
memcpy(code + superScalarHashOffset, codeShhInit, codeSshInitSize); memcpy(code + superScalarHashOffset, codeShhInit, codeSshInitSize);
codePos = superScalarHashOffset + codeSshInitSize; codePos = superScalarHashOffset + codeSshInitSize;
for (unsigned j = 0; j < N; ++j) { for (unsigned j = 0; j < N; ++j) {
@ -273,7 +269,7 @@ namespace RandomX {
Instruction& instr = prog(i); Instruction& instr = prog(i);
instr.src %= RegistersCount; instr.src %= RegistersCount;
instr.dst %= RegistersCount; instr.dst %= RegistersCount;
generateCode<SuperscalarProgram>(instr, i); generateSuperscalarCode(instr, reciprocalCache);
} }
emit(codeShhLoad, codeSshLoadSize); emit(codeShhLoad, codeSshLoadSize);
if (j < N - 1) { if (j < N - 1) {
@ -293,7 +289,7 @@ namespace RandomX {
} }
template template
void JitCompilerX86::generateSuperScalarHash(SuperscalarProgram(&programs)[RANDOMX_CACHE_ACCESSES]); void JitCompilerX86::generateSuperscalarHash(SuperscalarProgram(&programs)[RANDOMX_CACHE_ACCESSES], std::vector<uint64_t> &reciprocalCache);
void JitCompilerX86::generateDatasetInitCode() { void JitCompilerX86::generateDatasetInitCode() {
memcpy(code, codeDatasetInit, datasetInitSize); memcpy(code, codeDatasetInit, datasetInitSize);
@ -314,7 +310,12 @@ namespace RandomX {
emitByte(0xc0 + pcfg.readReg1); emitByte(0xc0 + pcfg.readReg1);
memcpy(code + codePos, codeLoopLoad, loopLoadSize); memcpy(code + codePos, codeLoopLoad, loopLoadSize);
codePos += loopLoadSize; codePos += loopLoadSize;
generateCode(prog); for (unsigned i = 0; i < prog.getSize(); ++i) {
Instruction& instr = prog(i);
instr.src %= RegistersCount;
instr.dst %= RegistersCount;
generateCode(instr, i);
}
emit(REX_MOV_RR); emit(REX_MOV_RR);
emitByte(0xc0 + pcfg.readReg2); emitByte(0xc0 + pcfg.readReg2);
emit(REX_XOR_EAX); emit(REX_XOR_EAX);
@ -331,7 +332,6 @@ namespace RandomX {
emit32(epilogueOffset - codePos - 4); emit32(epilogueOffset - codePos - 4);
} }
template<class P>
void JitCompilerX86::generateCode(Instruction& instr, int i) { void JitCompilerX86::generateCode(Instruction& instr, int i) {
#ifdef RANDOMX_JUMP #ifdef RANDOMX_JUMP
instructionOffsets.push_back(codePos); instructionOffsets.push_back(codePos);
@ -340,67 +340,66 @@ namespace RandomX {
(this->*generator)(instr, i); (this->*generator)(instr, i);
} }
template<> void JitCompilerX86::generateSuperscalarCode(Instruction& instr, std::vector<uint64_t> &reciprocalCache) {
void JitCompilerX86::generateCode<SuperscalarProgram>(Instruction& instr, int i) {
switch (instr.opcode) switch (instr.opcode)
{ {
case RandomX::SuperscalarInstructionType::ISUB_R: case randomx::SuperscalarInstructionType::ISUB_R:
emit(REX_SUB_RR); emit(REX_SUB_RR);
emitByte(0xc0 + 8 * instr.dst + instr.src); emitByte(0xc0 + 8 * instr.dst + instr.src);
break; break;
case RandomX::SuperscalarInstructionType::IXOR_R: case randomx::SuperscalarInstructionType::IXOR_R:
emit(REX_XOR_RR); emit(REX_XOR_RR);
emitByte(0xc0 + 8 * instr.dst + instr.src); emitByte(0xc0 + 8 * instr.dst + instr.src);
break; break;
case RandomX::SuperscalarInstructionType::IADD_RS: case randomx::SuperscalarInstructionType::IADD_RS:
emit(REX_LEA); emit(REX_LEA);
emitByte(0x04 + 8 * instr.dst); emitByte(0x04 + 8 * instr.dst);
genSIB(instr.getModShift2(), instr.src, instr.dst); genSIB(instr.getModShift2(), instr.src, instr.dst);
break; break;
case RandomX::SuperscalarInstructionType::IMUL_R: case randomx::SuperscalarInstructionType::IMUL_R:
emit(REX_IMUL_RR); emit(REX_IMUL_RR);
emitByte(0xc0 + 8 * instr.dst + instr.src); emitByte(0xc0 + 8 * instr.dst + instr.src);
break; break;
case RandomX::SuperscalarInstructionType::IROR_C: case randomx::SuperscalarInstructionType::IROR_C:
emit(REX_ROT_I8); emit(REX_ROT_I8);
emitByte(0xc8 + instr.dst); emitByte(0xc8 + instr.dst);
emitByte(instr.getImm32() & 63); emitByte(instr.getImm32() & 63);
break; break;
case RandomX::SuperscalarInstructionType::IADD_C7: case randomx::SuperscalarInstructionType::IADD_C7:
emit(REX_81); emit(REX_81);
emitByte(0xc0 + instr.dst); emitByte(0xc0 + instr.dst);
emit32(instr.getImm32()); emit32(instr.getImm32());
break; break;
case RandomX::SuperscalarInstructionType::IXOR_C7: case randomx::SuperscalarInstructionType::IXOR_C7:
emit(REX_XOR_RI); emit(REX_XOR_RI);
emitByte(0xf0 + instr.dst); emitByte(0xf0 + instr.dst);
emit32(instr.getImm32()); emit32(instr.getImm32());
break; break;
case RandomX::SuperscalarInstructionType::IADD_C8: case randomx::SuperscalarInstructionType::IADD_C8:
emit(REX_81); emit(REX_81);
emitByte(0xc0 + instr.dst); emitByte(0xc0 + instr.dst);
emit32(instr.getImm32()); emit32(instr.getImm32());
emit(NOP1); emit(NOP1);
break; break;
case RandomX::SuperscalarInstructionType::IXOR_C8: case randomx::SuperscalarInstructionType::IXOR_C8:
emit(REX_XOR_RI); emit(REX_XOR_RI);
emitByte(0xf0 + instr.dst); emitByte(0xf0 + instr.dst);
emit32(instr.getImm32()); emit32(instr.getImm32());
emit(NOP1); emit(NOP1);
break; break;
case RandomX::SuperscalarInstructionType::IADD_C9: case randomx::SuperscalarInstructionType::IADD_C9:
emit(REX_81); emit(REX_81);
emitByte(0xc0 + instr.dst); emitByte(0xc0 + instr.dst);
emit32(instr.getImm32()); emit32(instr.getImm32());
emit(NOP2); emit(NOP2);
break; break;
case RandomX::SuperscalarInstructionType::IXOR_C9: case randomx::SuperscalarInstructionType::IXOR_C9:
emit(REX_XOR_RI); emit(REX_XOR_RI);
emitByte(0xf0 + instr.dst); emitByte(0xf0 + instr.dst);
emit32(instr.getImm32()); emit32(instr.getImm32());
emit(NOP2); emit(NOP2);
break; break;
case RandomX::SuperscalarInstructionType::IMULH_R: case randomx::SuperscalarInstructionType::IMULH_R:
emit(REX_MOV_RR64); emit(REX_MOV_RR64);
emitByte(0xc0 + instr.dst); emitByte(0xc0 + instr.dst);
emit(REX_MUL_R); emit(REX_MUL_R);
@ -408,7 +407,7 @@ namespace RandomX {
emit(REX_MOV_R64R); emit(REX_MOV_R64R);
emitByte(0xc2 + 8 * instr.dst); emitByte(0xc2 + 8 * instr.dst);
break; break;
case RandomX::SuperscalarInstructionType::ISMULH_R: case randomx::SuperscalarInstructionType::ISMULH_R:
emit(REX_MOV_RR64); emit(REX_MOV_RR64);
emitByte(0xc0 + instr.dst); emitByte(0xc0 + instr.dst);
emit(REX_MUL_R); emit(REX_MUL_R);
@ -416,9 +415,9 @@ namespace RandomX {
emit(REX_MOV_R64R); emit(REX_MOV_R64R);
emitByte(0xc2 + 8 * instr.dst); emitByte(0xc2 + 8 * instr.dst);
break; break;
case RandomX::SuperscalarInstructionType::IMUL_RCP: case randomx::SuperscalarInstructionType::IMUL_RCP:
emit(MOV_RAX_I); emit(MOV_RAX_I);
emit64(reciprocal(instr.getImm32())); emit64(reciprocalCache[instr.getImm32()]);
emit(REX_IMUL_RM); emit(REX_IMUL_RM);
emitByte(0xc0 + 8 * instr.dst); emitByte(0xc0 + 8 * instr.dst);
break; break;
@ -427,8 +426,6 @@ namespace RandomX {
} }
} }
template void JitCompilerX86::generateCode<Program>(Instruction& instr, int i);
void JitCompilerX86::genAddressReg(Instruction& instr, bool rax = true) { void JitCompilerX86::genAddressReg(Instruction& instr, bool rax = true) {
emit(LEA_32); emit(LEA_32);
emitByte(0x80 + instr.src + (rax ? 0 : 8)); emitByte(0x80 + instr.src + (rax ? 0 : 8));

View File

@ -21,10 +21,11 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
#include "common.hpp" #include "common.hpp"
#include "Instruction.hpp" #include "Instruction.hpp"
#include "superscalar_program.hpp"
#include <cstring> #include <cstring>
#include <vector> #include <vector>
namespace RandomX { namespace randomx {
class Program; class Program;
class ProgramConfiguration; class ProgramConfiguration;
@ -40,15 +41,14 @@ namespace RandomX {
JitCompilerX86(); JitCompilerX86();
~JitCompilerX86(); ~JitCompilerX86();
void generateProgram(Program&, ProgramConfiguration&); void generateProgram(Program&, ProgramConfiguration&);
template<bool superscalar>
void generateProgramLight(Program&, ProgramConfiguration&); void generateProgramLight(Program&, ProgramConfiguration&);
template<size_t N> template<size_t N>
void generateSuperScalarHash(SuperscalarProgram (&programs)[N]); void generateSuperscalarHash(SuperscalarProgram (&programs)[N], std::vector<uint64_t> &);
void generateDatasetInitCode();
ProgramFunc getProgramFunc() { ProgramFunc getProgramFunc() {
return (ProgramFunc)code; return (ProgramFunc)code;
} }
DatasetInitFunc getDatasetInitFunc() { DatasetInitFunc getDatasetInitFunc() {
generateDatasetInitCode();
return (DatasetInitFunc)code; return (DatasetInitFunc)code;
} }
uint8_t* getCode() { uint8_t* getCode() {
@ -62,18 +62,6 @@ namespace RandomX {
uint8_t* code; uint8_t* code;
int32_t codePos; int32_t codePos;
template<class P>
void generateCode(P& prog) {
for (unsigned i = 0; i < prog.getSize(); ++i) {
Instruction& instr = prog(i);
instr.src %= RegistersCount;
instr.dst %= RegistersCount;
generateCode<P>(instr, i);
}
}
void generateDatasetInitCode();
void generateProgramPrologue(Program&, ProgramConfiguration&); void generateProgramPrologue(Program&, ProgramConfiguration&);
void generateProgramEpilogue(Program&); void generateProgramEpilogue(Program&);
int getConditionRegister(); int getConditionRegister();
@ -84,8 +72,8 @@ namespace RandomX {
void handleCondition(Instruction&, int); void handleCondition(Instruction&, int);
template<class P>
void generateCode(Instruction&, int); void generateCode(Instruction&, int);
void generateSuperscalarCode(Instruction &, std::vector<uint64_t> &);
void emitByte(uint8_t val) { void emitByte(uint8_t val) {
code[codePos] = val; code[codePos] = val;

View File

@ -25,7 +25,7 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
#include "Instruction.hpp" #include "Instruction.hpp"
#include "blake2/endian.h" #include "blake2/endian.h"
namespace RandomX { namespace randomx {
struct ProgramConfiguration { struct ProgramConfiguration {
uint64_t eMask[2]; uint64_t eMask[2];
@ -59,46 +59,4 @@ namespace RandomX {
}; };
static_assert(sizeof(Program) % 64 == 0, "Invalid size of class Program"); static_assert(sizeof(Program) % 64 == 0, "Invalid size of class Program");
class SuperscalarProgram {
public:
Instruction& operator()(int pc) {
return programBuffer[pc];
}
friend std::ostream& operator<<(std::ostream& os, const SuperscalarProgram& p) {
p.print(os);
return os;
}
uint32_t getSize() {
return size;
}
void setSize(uint32_t val) {
size = val;
}
int getAddressRegister() {
return addrReg;
}
void setAddressRegister(uint32_t val) {
addrReg = val;
}
double ipc;
int codeSize;
int macroOps;
int decodeCycles;
int cpuLatency;
int asicLatency;
int mulCount;
int cpuLatencies[8];
int asicLatencies[8];
private:
void print(std::ostream& os) const {
for (unsigned i = 0; i < size; ++i) {
auto instr = programBuffer[i];
os << instr;
}
}
Instruction programBuffer[RANDOMX_SUPERSCALAR_MAX_SIZE];
uint32_t size;
int addrReg;
};
} }

View File

@ -24,9 +24,60 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
#include <cstring> #include <cstring>
#include <iomanip> #include <iomanip>
#include "intrinPortable.h" #include "intrinPortable.h"
#include "allocator.hpp"
std::ostream& operator<<(std::ostream& os, const RandomX::RegisterFile& rf) { randomx_vm::~randomx_vm() {
for (int i = 0; i < RandomX::RegistersCount; ++i)
}
void randomx_vm::resetRoundingMode() {
initFpu();
}
constexpr int mantissaSize = 52;
constexpr int exponentSize = 11;
constexpr uint64_t mantissaMask = (1ULL << mantissaSize) - 1;
constexpr uint64_t exponentMask = (1ULL << exponentSize) - 1;
constexpr int exponentBias = 1023;
static inline uint64_t getSmallPositiveFloatBits(uint64_t entropy) {
auto exponent = entropy >> 59; //0..31
auto mantissa = entropy & mantissaMask;
exponent += exponentBias;
exponent &= exponentMask;
exponent <<= mantissaSize;
return exponent | mantissa;
}
void randomx_vm::initialize() {
store64(&reg.a[0].lo, getSmallPositiveFloatBits(program.getEntropy(0)));
store64(&reg.a[0].hi, getSmallPositiveFloatBits(program.getEntropy(1)));
store64(&reg.a[1].lo, getSmallPositiveFloatBits(program.getEntropy(2)));
store64(&reg.a[1].hi, getSmallPositiveFloatBits(program.getEntropy(3)));
store64(&reg.a[2].lo, getSmallPositiveFloatBits(program.getEntropy(4)));
store64(&reg.a[2].hi, getSmallPositiveFloatBits(program.getEntropy(5)));
store64(&reg.a[3].lo, getSmallPositiveFloatBits(program.getEntropy(6)));
store64(&reg.a[3].hi, getSmallPositiveFloatBits(program.getEntropy(7)));
mem.ma = program.getEntropy(8) & randomx::CacheLineAlignMask;
mem.mx = program.getEntropy(10);
auto addressRegisters = program.getEntropy(12);
config.readReg0 = 0 + (addressRegisters & 1);
addressRegisters >>= 1;
config.readReg1 = 2 + (addressRegisters & 1);
addressRegisters >>= 1;
config.readReg2 = 4 + (addressRegisters & 1);
addressRegisters >>= 1;
config.readReg3 = 6 + (addressRegisters & 1);
//datasetBase = program.getEntropy(13) % datasetRange;
constexpr uint64_t mask22bit = (1ULL << 22) - 1;
constexpr uint64_t maskExp240 = ieee_get_exponent_mask<-240>();
store64(&config.eMask[0], (program.getEntropy(14) & mask22bit) | maskExp240);
store64(&config.eMask[1], (program.getEntropy(15) & mask22bit) | maskExp240);
}
//TODO
std::ostream& operator<<(std::ostream& os, const randomx::RegisterFile& rf) {
for (int i = 0; i < randomx::RegistersCount; ++i)
os << std::hex << "r" << i << " = " << rf.r[i] << std::endl << std::dec; os << std::hex << "r" << i << " = " << rf.r[i] << std::endl << std::dec;
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
os << std::hex << "f" << i << " = " << *(uint64_t*)&rf.f[i].hi << " (" << rf.f[i].hi << ")" << std::endl os << std::hex << "f" << i << " = " << *(uint64_t*)&rf.f[i].hi << " (" << rf.f[i].hi << ")" << std::endl
@ -40,66 +91,32 @@ std::ostream& operator<<(std::ostream& os, const RandomX::RegisterFile& rf) {
return os; return os;
} }
namespace RandomX { namespace randomx {
constexpr int mantissaSize = 52; template<class Allocator, bool softAes>
constexpr int exponentSize = 11; VmBase<Allocator, softAes>::~VmBase() {
constexpr uint64_t mantissaMask = (1ULL << mantissaSize) - 1; Allocator::freeMemory(scratchpad, ScratchpadSize);
constexpr uint64_t exponentMask = (1ULL << exponentSize) - 1;
constexpr int exponentBias = 1023;
static inline uint64_t getSmallPositiveFloatBits(uint64_t entropy) {
auto exponent = entropy >> 59; //0..31
auto mantissa = entropy & mantissaMask;
exponent += exponentBias;
exponent &= exponentMask;
exponent <<= mantissaSize;
return exponent | mantissa;
} }
VirtualMachine::VirtualMachine() { template<class Allocator, bool softAes>
mem.ds.dataset.memory = nullptr; bool VmBase<Allocator, softAes>::allocate() {
scratchpad = (uint8_t*)Allocator::allocMemory(ScratchpadSize);
return scratchpad != nullptr;
} }
void VirtualMachine::resetRoundingMode() { template<class Allocator, bool softAes>
initFpu(); void VmBase<Allocator, softAes>::generate(void* seed, void* buffer, size_t bufferSize) {
fillAes1Rx4<softAes>(seed, bufferSize, buffer);
} }
void VirtualMachine::initialize() { template<class Allocator, bool softAes>
store64(&reg.a[0].lo, getSmallPositiveFloatBits(program.getEntropy(0))); void VmBase<Allocator, softAes>::getFinalResult(void* out, size_t outSize) {
store64(&reg.a[0].hi, getSmallPositiveFloatBits(program.getEntropy(1))); hashAes1Rx4<softAes>(scratchpad, ScratchpadSize, &reg.a);
store64(&reg.a[1].lo, getSmallPositiveFloatBits(program.getEntropy(2))); blake2b(out, outSize, &reg, sizeof(RegisterFile), nullptr, 0);
store64(&reg.a[1].hi, getSmallPositiveFloatBits(program.getEntropy(3)));
store64(&reg.a[2].lo, getSmallPositiveFloatBits(program.getEntropy(4)));
store64(&reg.a[2].hi, getSmallPositiveFloatBits(program.getEntropy(5)));
store64(&reg.a[3].lo, getSmallPositiveFloatBits(program.getEntropy(6)));
store64(&reg.a[3].hi, getSmallPositiveFloatBits(program.getEntropy(7)));
mem.ma = program.getEntropy(8) & CacheLineAlignMask;
mem.mx = program.getEntropy(10);
auto addressRegisters = program.getEntropy(12);
config.readReg0 = 0 + (addressRegisters & 1);
addressRegisters >>= 1;
config.readReg1 = 2 + (addressRegisters & 1);
addressRegisters >>= 1;
config.readReg2 = 4 + (addressRegisters & 1);
addressRegisters >>= 1;
config.readReg3 = 6 + (addressRegisters & 1);
datasetBase = program.getEntropy(13) % datasetRange;
constexpr uint64_t mask22bit = (1ULL << 22) - 1;
constexpr uint64_t maskExp240 = ieee_get_exponent_mask<-240>();
store64(&config.eMask[0], (program.getEntropy(14) & mask22bit) | maskExp240);
store64(&config.eMask[1], (program.getEntropy(15) & mask22bit) | maskExp240);
} }
template<bool softAes> template class VmBase<AlignedAllocator<CacheLineSize>, false>;
void VirtualMachine::getResult(void* scratchpad, size_t scratchpadSize, void* outHash) { template class VmBase<AlignedAllocator<CacheLineSize>, true>;
if (scratchpadSize > 0) { template class VmBase<LargePageAllocator, false>;
hashAes1Rx4<softAes>(scratchpad, scratchpadSize, &reg.a); template class VmBase<LargePageAllocator, true>;
}
blake2b(outHash, ResultSize, &reg, sizeof(RegisterFile), nullptr, 0);
}
template void VirtualMachine::getResult<false>(void* scratchpad, size_t scratchpadSize, void* outHash);
template void VirtualMachine::getResult<true>(void* scratchpad, size_t scratchpadSize, void* outHash);
} }

View File

@ -18,38 +18,40 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include "common.hpp" #include "common.hpp"
#include "dataset.hpp"
#include "Program.hpp" #include "Program.hpp"
namespace RandomX { /* Global namespace for C binding */
struct randomx_vm {
class VirtualMachine { virtual ~randomx_vm() = 0;
public: virtual bool allocate() = 0;
VirtualMachine(); virtual void generate(void* seed, void* buffer, size_t bufferSize) = 0;
virtual ~VirtualMachine() {}
virtual void setDataset(dataset_t ds, uint64_t size, SuperscalarProgram (&programs)[RANDOMX_CACHE_ACCESSES]) = 0;
void setScratchpad(void* ptr) {
scratchpad = (uint8_t*)ptr;
}
void resetRoundingMode(); void resetRoundingMode();
virtual void initialize(); virtual void initialize();
virtual void execute() = 0; virtual void execute() = 0;
template<bool softAes> virtual void getFinalResult(void* out, size_t outSize) = 0;
void getResult(void* scratchpad, size_t scratchpadSize, void* outHash); virtual void setDataset(randomx_dataset* dataset) { }
const RegisterFile& getRegisterFile() { virtual void setCache(randomx_cache* cache) { }
return reg;
} alignas(64) randomx::Program program;
Program* getProgramBuffer() { alignas(64) randomx::RegisterFile reg;
return &program; alignas(16) randomx::ProgramConfiguration config;
} randomx::MemoryRegisters mem;
protected:
alignas(64) Program program;
alignas(64) RegisterFile reg;
alignas(16) ProgramConfiguration config;
MemoryRegisters mem;
uint8_t* scratchpad; uint8_t* scratchpad;
uint32_t datasetRange; };
uint32_t datasetBase;
namespace randomx {
template<class Allocator, bool softAes>
class VmBase : public randomx_vm {
public:
~VmBase() override;
bool allocate() override;
void generate(void* seed, void* buffer, size_t bufferSize) override;
void getFinalResult(void* out, size_t outSize) override;
}; };
} }

52
src/allocator.cpp Normal file
View File

@ -0,0 +1,52 @@
/*
Copyright (c) 2019 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/>.
*/
#pragma once
#include "allocator.hpp"
#include "virtualMemory.hpp"
#include "intrinPortable.h"
#include "common.hpp"
namespace randomx {
template<size_t alignment>
void* AlignedAllocator<alignment>::allocMemory(size_t count) {
return _mm_malloc(count, alignment);
}
template<size_t alignment>
void AlignedAllocator<alignment>::freeMemory(void* ptr, size_t count) {
_mm_free(ptr);
}
template void* AlignedAllocator<CacheLineSize>::allocMemory(size_t count);
template void AlignedAllocator<CacheLineSize>::freeMemory(void* ptr, size_t count);
template void* AlignedAllocator<sizeof(__m128i)>::allocMemory(size_t count);
template void AlignedAllocator<sizeof(__m128i)>::freeMemory(void* ptr, size_t count);
void* LargePageAllocator::allocMemory(size_t count) {
return allocLargePagesMemory(count);
}
void LargePageAllocator::freeMemory(void* ptr, size_t count) {
freePagedMemory(ptr, count);
};
}

37
src/allocator.hpp Normal file
View File

@ -0,0 +1,37 @@
/*
Copyright (c) 2019 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/>.
*/
#pragma once
#include <cstddef>
namespace randomx {
template<size_t alignment>
struct AlignedAllocator {
static void* allocMemory(size_t);
static void freeMemory(void*, size_t);
};
struct LargePageAllocator {
static void* allocMemory(size_t);
static void freeMemory(void*, size_t);
};
}

View File

@ -23,8 +23,9 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
#include <iostream> #include <iostream>
#include "blake2/endian.h" #include "blake2/endian.h"
#include "configuration.h" #include "configuration.h"
#include "randomx.h"
namespace RandomX { namespace randomx {
static_assert((RANDOMX_ARGON_MEMORY & (RANDOMX_ARGON_MEMORY - 1)) == 0, "RANDOMX_ARGON_MEMORY must be a power of 2."); static_assert((RANDOMX_ARGON_MEMORY & (RANDOMX_ARGON_MEMORY - 1)) == 0, "RANDOMX_ARGON_MEMORY must be a power of 2.");
static_assert((RANDOMX_DATASET_SIZE & (RANDOMX_DATASET_SIZE - 1)) == 0, "RANDOMX_DATASET_SIZE must be a power of 2."); static_assert((RANDOMX_DATASET_SIZE & (RANDOMX_DATASET_SIZE - 1)) == 0, "RANDOMX_DATASET_SIZE must be a power of 2.");
@ -58,6 +59,7 @@ namespace RandomX {
constexpr int ArgonBlockSize = 1024; constexpr int ArgonBlockSize = 1024;
constexpr int ArgonSaltSize = sizeof(RANDOMX_ARGON_SALT) - 1; constexpr int ArgonSaltSize = sizeof(RANDOMX_ARGON_SALT) - 1;
constexpr int CacheLineSize = 64; constexpr int CacheLineSize = 64;
constexpr int ScratchpadSize = RANDOMX_SCRATCHPAD_L3;
constexpr uint32_t CacheLineAlignMask = (RANDOMX_DATASET_SIZE - 1) & ~(CacheLineSize - 1); constexpr uint32_t CacheLineAlignMask = (RANDOMX_DATASET_SIZE - 1) & ~(CacheLineSize - 1);
constexpr uint32_t CacheSize = RANDOMX_ARGON_MEMORY * 1024; constexpr uint32_t CacheSize = RANDOMX_ARGON_MEMORY * 1024;
constexpr int CacheBlockCount = CacheSize / CacheLineSize; constexpr int CacheBlockCount = CacheSize / CacheLineSize;
@ -98,39 +100,9 @@ namespace RandomX {
constexpr int RegisterNeedsDisplacement = 5; //x86 r13 register constexpr int RegisterNeedsDisplacement = 5; //x86 r13 register
constexpr int RegisterNeedsSib = 4; //x86 r12 register constexpr int RegisterNeedsSib = 4; //x86 r12 register
struct Cache {
uint8_t* memory;
uint64_t size;
};
struct Dataset : public Cache {
};
class ILightClientAsyncWorker {
public:
virtual ~ILightClientAsyncWorker() {}
virtual void prepareBlock(addr_t) = 0;
virtual void prepareBlocks(void* out, uint32_t startBlock, uint32_t blockCount) = 0;
virtual const uint64_t* getBlock(addr_t) = 0;
virtual void getBlocks(void* out, uint32_t startBlock, uint32_t blockCount) = 0;
virtual void sync() = 0;
const Cache& getCache() {
return cache;
}
protected:
ILightClientAsyncWorker(const Cache& c) : cache(c) {}
const Cache& cache;
};
union dataset_t {
Dataset dataset;
Cache cache;
ILightClientAsyncWorker* asyncWorker;
};
struct MemoryRegisters { struct MemoryRegisters {
addr_t mx, ma; addr_t mx, ma;
dataset_t ds; uint8_t* memory = nullptr;
}; };
struct RegisterFile { struct RegisterFile {
@ -141,9 +113,8 @@ namespace RandomX {
}; };
typedef void(*DatasetReadFunc)(addr_t, MemoryRegisters&, int_reg_t(&reg)[RegistersCount]); typedef void(*DatasetReadFunc)(addr_t, MemoryRegisters&, int_reg_t(&reg)[RegistersCount]);
typedef void(*ProgramFunc)(RegisterFile&, MemoryRegisters&, uint8_t* /* scratchpad */, uint64_t); typedef void(*ProgramFunc)(RegisterFile&, MemoryRegisters&, uint8_t* /* scratchpad */, uint64_t);
typedef void(*DatasetInitFunc)(uint8_t* cache, uint8_t* dataset, uint32_t startBlock, uint32_t endBlock); typedef void(*DatasetInitFunc)(randomx_cache* cache, uint8_t* dataset, uint32_t startBlock, uint32_t endBlock);
} }
std::ostream& operator<<(std::ostream& os, const RandomX::RegisterFile& rf); std::ostream& operator<<(std::ostream& os, const randomx::RegisterFile& rf);

View File

@ -22,14 +22,17 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
#include <stdexcept> #include <stdexcept>
#include <cstring> #include <cstring>
#include <limits> #include <limits>
#include <cstring>
#include "common.hpp" #include "common.hpp"
#include "dataset.hpp" #include "dataset.hpp"
#include "Cache.hpp"
#include "virtualMemory.hpp" #include "virtualMemory.hpp"
#include "softAes.h" #include "superscalarGenerator.hpp"
#include "squareHash.h" #include "Blake2Generator.hpp"
#include "reciprocal.h"
#include "blake2/endian.h" #include "blake2/endian.h"
#include "argon2.h"
#include "argon2_core.h"
#if defined(__SSE2__) #if defined(__SSE2__)
#include <wmmintrin.h> #include <wmmintrin.h>
@ -38,113 +41,174 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
#define PREFETCH(memory) #define PREFETCH(memory)
#endif #endif
namespace RandomX { randomx_dataset::~randomx_dataset() {
#if true //RANDOMX_ARGON_GROWTH != 0 || (!defined(_M_X64) && !defined(__x86_64__)) }
static FORCE_INLINE uint8_t* selectMixBlock(const Cache& cache, uint64_t& currentIndex, uint64_t& nextIndex) {
uint8_t* mixBlock; static_assert(RANDOMX_ARGON_MEMORY % (RANDOMX_ARGON_LANES * ARGON2_SYNC_POINTS) == 0, "RANDOMX_ARGON_MEMORY - invalid value");
if (RANDOMX_ARGON_GROWTH == 0) {
void randomx_cache::initialize(const void *seed, size_t seedSize) {
uint32_t memory_blocks, segment_length;
argon2_instance_t instance;
argon2_context context;
context.out = nullptr;
context.outlen = 0;
context.pwd = CONST_CAST(uint8_t *)seed;
context.pwdlen = (uint32_t)seedSize;
context.salt = CONST_CAST(uint8_t *)RANDOMX_ARGON_SALT;
context.saltlen = (uint32_t)randomx::ArgonSaltSize;
context.secret = NULL;
context.secretlen = 0;
context.ad = NULL;
context.adlen = 0;
context.t_cost = RANDOMX_ARGON_ITERATIONS;
context.m_cost = RANDOMX_ARGON_MEMORY;
context.lanes = RANDOMX_ARGON_LANES;
context.threads = 1;
context.allocate_cbk = NULL;
context.free_cbk = NULL;
context.flags = ARGON2_DEFAULT_FLAGS;
context.version = ARGON2_VERSION_NUMBER;
/* 2. Align memory size */
/* Minimum memory_blocks = 8L blocks, where L is the number of lanes */
memory_blocks = context.m_cost;
segment_length = memory_blocks / (context.lanes * ARGON2_SYNC_POINTS);
instance.version = context.version;
instance.memory = NULL;
instance.passes = context.t_cost;
instance.memory_blocks = memory_blocks;
instance.segment_length = segment_length;
instance.lane_length = segment_length * ARGON2_SYNC_POINTS;
instance.lanes = context.lanes;
instance.threads = context.threads;
instance.type = Argon2_d;
instance.memory = (block*)memory;
if (instance.threads > instance.lanes) {
instance.threads = instance.lanes;
}
/* 3. Initialization: Hashing inputs, allocating memory, filling first
* blocks
*/
argon_initialize(&instance, &context);
fill_memory_blocks(&instance);
reciprocalCache.clear();
randomx::Blake2Generator gen(seed, 1000);
for (int i = 0; i < RANDOMX_CACHE_ACCESSES; ++i) {
randomx::generateSuperscalar(programs[i], gen);
for (unsigned j = 0; j < programs[i].getSize(); ++j) {
auto& instr = programs[i](j);
if (instr.opcode == randomx::SuperscalarInstructionType::IMUL_RCP) {
auto rcp = reciprocal(instr.getImm32());
instr.setImm32(reciprocalCache.size());
reciprocalCache.push_back(rcp);
}
}
}
}
namespace randomx {
template<class Allocator>
bool Dataset<Allocator>::allocate() {
memory = (uint8_t*)Allocator::allocMemory(RANDOMX_DATASET_SIZE);
return true;
}
template<class Allocator>
Dataset<Allocator>::~Dataset() {
Allocator::freeMemory(memory, RANDOMX_DATASET_SIZE);
}
template<class Allocator>
bool Cache<Allocator>::allocate() {
memory = (uint8_t*)Allocator::allocMemory(RANDOMX_ARGON_MEMORY * ARGON2_BLOCK_SIZE);
return true;
}
template<class Allocator>
Cache<Allocator>::~Cache() {
Allocator::freeMemory(memory, RANDOMX_ARGON_MEMORY * ARGON2_BLOCK_SIZE);
}
template<class Allocator>
DatasetInitFunc Cache<Allocator>::getInitFunc() {
return &initDataset;
}
template<class Allocator>
DatasetInitFunc CacheWithJit<Allocator>::getInitFunc() {
return jit.getDatasetInitFunc();
}
template<class Allocator>
void CacheWithJit<Allocator>::initialize(const void *seed, size_t seedSize) {
randomx_cache::initialize(seed, seedSize);
jit.generateSuperscalarHash(programs, reciprocalCache);
jit.generateDatasetInitCode();
}
template class Dataset<AlignedAllocator<CacheLineSize>>;
template class Dataset<LargePageAllocator>;
template class Cache<AlignedAllocator<CacheLineSize>>;
template class Cache<LargePageAllocator>;
template class CacheWithJit<AlignedAllocator<CacheLineSize>>;
template class CacheWithJit<LargePageAllocator>;
constexpr uint64_t superscalarMul0 = 6364136223846793005ULL;
constexpr uint64_t superscalarAdd1 = 9298410992540426748ULL;
constexpr uint64_t superscalarAdd2 = 12065312585734608966ULL;
constexpr uint64_t superscalarAdd3 = 9306329213124610396ULL;
constexpr uint64_t superscalarAdd4 = 5281919268842080866ULL;
constexpr uint64_t superscalarAdd5 = 10536153434571861004ULL;
constexpr uint64_t superscalarAdd6 = 3398623926847679864ULL;
constexpr uint64_t superscalarAdd7 = 9549104520008361294ULL;
static inline uint8_t* getMixBlock(uint64_t registerValue, uint8_t *memory) {
constexpr uint32_t mask = (RANDOMX_ARGON_MEMORY * ArgonBlockSize / CacheLineSize - 1); constexpr uint32_t mask = (RANDOMX_ARGON_MEMORY * ArgonBlockSize / CacheLineSize - 1);
mixBlock = cache.memory + (currentIndex & mask) * CacheLineSize; return memory + (registerValue & mask) * CacheLineSize;
}
else {
const uint32_t modulus = cache.size / CacheLineSize;
mixBlock = cache.memory + (currentIndex % modulus) * CacheLineSize;
}
PREFETCHNTA(mixBlock);
nextIndex = squareHash(currentIndex + nextIndex);
return mixBlock;
} }
static FORCE_INLINE void mixCache(uint8_t* mixBlock, uint64_t& c0, uint64_t& c1, uint64_t& c2, uint64_t& c3, uint64_t& c4, uint64_t& c5, uint64_t& c6, uint64_t& c7) { void initDatasetBlock(randomx_cache* cache, uint8_t* out, uint64_t blockNumber) {
c0 ^= load64(mixBlock + 0); int_reg_t rl[8];
c1 ^= load64(mixBlock + 8);
c2 ^= load64(mixBlock + 16);
c3 ^= load64(mixBlock + 24);
c4 ^= load64(mixBlock + 32);
c5 ^= load64(mixBlock + 40);
c6 ^= load64(mixBlock + 48);
c7 ^= load64(mixBlock + 56);
}
void initBlock(const Cache& cache, uint8_t* out, uint64_t blockNumber, unsigned iterations) {
uint64_t c0, c1, c2, c3, c4, c5, c6, c7;
c0 = blockNumber;
c1 = c2 = c3 = c4 = c5 = c6 = c7 = 0;
uint8_t* mixBlock; uint8_t* mixBlock;
uint64_t registerValue = blockNumber;
rl[0] = (blockNumber + 1) * superscalarMul0;
rl[1] = rl[0] ^ superscalarAdd1;
rl[2] = rl[0] ^ superscalarAdd2;
rl[3] = rl[0] ^ superscalarAdd3;
rl[4] = rl[0] ^ superscalarAdd4;
rl[5] = rl[0] ^ superscalarAdd5;
rl[6] = rl[0] ^ superscalarAdd6;
rl[7] = rl[0] ^ superscalarAdd7;
for (unsigned i = 0; i < RANDOMX_CACHE_ACCESSES; ++i) {
mixBlock = getMixBlock(registerValue, cache->memory);
SuperscalarProgram& prog = cache->programs[i];
for (auto i = 0; i < iterations; ++i) { executeSuperscalar(rl, prog, &cache->reciprocalCache);
mixBlock = selectMixBlock(cache, c0, c1);
mixCache(mixBlock, c0, c1, c2, c3, c4, c5, c6, c7);
mixBlock = selectMixBlock(cache, c1, c2); for (unsigned q = 0; q < 8; ++q)
mixCache(mixBlock, c0, c1, c2, c3, c4, c5, c6, c7); rl[q] ^= load64(mixBlock + 8 * q);
mixBlock = selectMixBlock(cache, c2, c3); registerValue = rl[prog.getAddressRegister()];
mixCache(mixBlock, c0, c1, c2, c3, c4, c5, c6, c7);
mixBlock = selectMixBlock(cache, c3, c4);
mixCache(mixBlock, c0, c1, c2, c3, c4, c5, c6, c7);
mixBlock = selectMixBlock(cache, c4, c5);
mixCache(mixBlock, c0, c1, c2, c3, c4, c5, c6, c7);
mixBlock = selectMixBlock(cache, c5, c6);
mixCache(mixBlock, c0, c1, c2, c3, c4, c5, c6, c7);
mixBlock = selectMixBlock(cache, c6, c7);
mixCache(mixBlock, c0, c1, c2, c3, c4, c5, c6, c7);
mixBlock = selectMixBlock(cache, c7, c0);
mixCache(mixBlock, c0, c1, c2, c3, c4, c5, c6, c7);
} }
store64(out + 0, c0); memcpy(out, &rl, CacheLineSize);
store64(out + 8, c1);
store64(out + 16, c2);
store64(out + 24, c3);
store64(out + 32, c4);
store64(out + 40, c5);
store64(out + 48, c6);
store64(out + 56, c7);
}
#endif
void datasetRead(addr_t addr, MemoryRegisters& memory, RegisterFile& reg) {
uint64_t* datasetLine = (uint64_t*)(memory.ds.dataset.memory + memory.ma);
memory.mx ^= addr;
memory.mx &= -64; //align to cache line
std::swap(memory.mx, memory.ma);
PREFETCHNTA(memory.ds.dataset.memory + memory.ma);
for (int i = 0; i < RegistersCount; ++i)
reg.r[i] ^= datasetLine[i];
} }
void datasetReadLight(addr_t addr, MemoryRegisters& memory, int_reg_t (&reg)[RegistersCount]) { void initDataset(randomx_cache* cache, uint8_t* dataset, uint32_t startBlock, uint32_t endBlock) {
memory.mx ^= addr; for (uint32_t blockNumber = startBlock; blockNumber < endBlock; ++blockNumber, dataset += CacheLineSize)
memory.mx &= CacheLineAlignMask; //align to cache line initDatasetBlock(cache, dataset, blockNumber);
Cache& cache = memory.ds.cache;
uint64_t datasetLine[CacheLineSize / sizeof(uint64_t)];
initBlock(cache, (uint8_t*)datasetLine, memory.ma / CacheLineSize, RANDOMX_CACHE_ACCESSES / 8);
for (int i = 0; i < RegistersCount; ++i)
reg[i] ^= datasetLine[i];
std::swap(memory.mx, memory.ma);
} }
void datasetReadLightAsync(addr_t addr, MemoryRegisters& memory, int_reg_t(&reg)[RegistersCount]) { /*void datasetAlloc(dataset_t& ds, bool largePages) {
ILightClientAsyncWorker* aw = memory.ds.asyncWorker;
const uint64_t* datasetLine = aw->getBlock(memory.ma);
for (int i = 0; i < RegistersCount; ++i)
reg[i] ^= datasetLine[i];
memory.mx ^= addr;
memory.mx &= CacheLineAlignMask; //align to cache line
std::swap(memory.mx, memory.ma);
aw->prepareBlock(memory.ma);
}
void datasetAlloc(dataset_t& ds, bool largePages) {
if (std::numeric_limits<size_t>::max() < RANDOMX_DATASET_SIZE) if (std::numeric_limits<size_t>::max() < RANDOMX_DATASET_SIZE)
throw std::runtime_error("Platform doesn't support enough memory for the dataset"); throw std::runtime_error("Platform doesn't support enough memory for the dataset");
if (largePages) { if (largePages) {
@ -158,14 +222,8 @@ namespace RandomX {
} }
} }
void datasetInit(Cache& cache, Dataset& ds, uint32_t startBlock, uint32_t blockCount) {
for (uint64_t i = startBlock; i < startBlock + blockCount; ++i) {
initBlock(cache, ds.memory + i * CacheLineSize, i, RANDOMX_CACHE_ACCESSES / 8);
}
}
void datasetInitCache(const void* seed, dataset_t& ds, bool largePages) { void datasetInitCache(const void* seed, dataset_t& ds, bool largePages) {
ds.cache.memory = allocCache(ds.cache.size, largePages); ds.cache.memory = allocCache(ds.cache.size, largePages);
argonFill(ds.cache, seed, SeedSize); argonFill(ds.cache, seed, SeedSize);
} }*/
} }

View File

@ -20,26 +20,62 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <vector>
#include "intrinPortable.h" #include "intrinPortable.h"
#include "common.hpp" #include "common.hpp"
#include "randomx.h"
#include "Program.hpp"
#include "superscalar_program.hpp"
#include "JitCompilerX86.hpp"
#include "allocator.hpp"
namespace RandomX { struct randomx_dataset {
virtual ~randomx_dataset() = 0;
virtual bool allocate() = 0;
uint8_t* memory = nullptr;
};
#if false //RANDOMX_ARGON_GROWTH == 0 && (defined(_M_X64) || defined(__x86_64__)) struct randomx_cache : public randomx_dataset {
extern "C" virtual randomx::DatasetInitFunc getInitFunc() = 0;
#endif virtual void initialize(const void *seed, size_t seedSize); //argon2
void initBlock(const Cache& cache, uint8_t* out, uint64_t blockNumber, unsigned iterations); randomx::SuperscalarProgram programs[RANDOMX_CACHE_ACCESSES];
std::vector<uint64_t> reciprocalCache;
};
void datasetAlloc(dataset_t& ds, bool largePages);
void datasetInit(Cache& cache, Dataset& ds, uint32_t startBlock, uint32_t blockCount);
void datasetRead(addr_t addr, MemoryRegisters& memory, RegisterFile&); namespace randomx {
void datasetInitCache(const void* seed, dataset_t& dataset, bool largePages); template<class Allocator>
struct Dataset : public randomx_dataset {
~Dataset() override;
bool allocate() override;
};
void datasetReadLight(addr_t addr, MemoryRegisters& memory, int_reg_t(&reg)[RegistersCount]); using DatasetDefault = Dataset<AlignedAllocator<CacheLineSize>>;
using DatasetLargePage = Dataset<LargePageAllocator>;
void datasetReadLightAsync(addr_t addr, MemoryRegisters& memory, int_reg_t(&reg)[RegistersCount]); template<class Allocator>
struct Cache : public randomx_cache {
~Cache() override;
bool allocate() override;
DatasetInitFunc getInitFunc() override;
};
template<class Allocator>
struct CacheWithJit : public Cache<Allocator> {
using Cache<Allocator>::programs;
using Cache<Allocator>::reciprocalCache;
void initialize(const void *seed, size_t seedSize) override;
DatasetInitFunc getInitFunc() override;
JitCompilerX86 jit;
};
using CacheDefault = Cache<AlignedAllocator<CacheLineSize>>;
using CacheWithJitDefault = CacheWithJit<AlignedAllocator<CacheLineSize>>;
using CacheLargePage = Cache<LargePageAllocator>;
using CacheWithJitLargePage = CacheWithJit<LargePageAllocator>;
void initDatasetBlock(randomx_cache* cache, uint8_t* out, uint64_t blockNumber);
void initDataset(randomx_cache* cache, uint8_t* dataset, uint32_t startBlock, uint32_t endBlock);
} }

View File

@ -17,31 +17,28 @@ You should have received a copy of the GNU General Public License
along with RandomX. If not, see<http://www.gnu.org/licenses/>. along with RandomX. If not, see<http://www.gnu.org/licenses/>.
*/ */
//#define TRACE //#define TRACE
#include "InterpretedVirtualMachine.hpp"
#include "CompiledVirtualMachine.hpp" //#include "AssemblyGeneratorX86.hpp"
#include "CompiledLightVirtualMachine.hpp"
#include "AssemblyGeneratorX86.hpp"
#include "Stopwatch.hpp" #include "Stopwatch.hpp"
#include "blake2/blake2.h" //#include "blake2/blake2.h"
#include "blake2/endian.h" #include "blake2/endian.h"
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
#include <exception> #include <exception>
#include <cstring> #include <cstring>
#include "Program.hpp" //#include "Program.hpp"
#include <string> #include <string>
#include <vector>
#include <thread> #include <thread>
#include <atomic> #include <atomic>
#include "dataset.hpp" //#include "hashAes1Rx4.hpp"
#include "Cache.hpp" //#include "JitCompilerX86.hpp"
#include "hashAes1Rx4.hpp" #include "randomx.h"
#include "superscalarGenerator.hpp"
#include "JitCompilerX86.hpp"
const uint8_t seed[32] = { 191, 182, 222, 175, 249, 89, 134, 104, 241, 68, 191, 62, 162, 166, 61, 64, 123, 191, 227, 193, 118, 60, 188, 53, 223, 133, 175, 24, 123, 230, 55, 74 }; const uint8_t seed[32] = { 191, 182, 222, 175, 249, 89, 134, 104, 241, 68, 191, 62, 162, 166, 61, 64, 123, 191, 227, 193, 118, 60, 188, 53, 223, 133, 175, 24, 123, 230, 55, 74 };
const uint8_t blockTemplate__[] = { const uint8_t blockTemplate_[] = {
0x07, 0x07, 0xf7, 0xa4, 0xf0, 0xd6, 0x05, 0xb3, 0x03, 0x26, 0x08, 0x16, 0xba, 0x3f, 0x10, 0x90, 0x2e, 0x1a, 0x14, 0x07, 0x07, 0xf7, 0xa4, 0xf0, 0xd6, 0x05, 0xb3, 0x03, 0x26, 0x08, 0x16, 0xba, 0x3f, 0x10, 0x90, 0x2e, 0x1a, 0x14,
0x5a, 0xc5, 0xfa, 0xd3, 0xaa, 0x3a, 0xf6, 0xea, 0x44, 0xc1, 0x18, 0x69, 0xdc, 0x4f, 0x85, 0x3f, 0x00, 0x2b, 0x2e, 0x5a, 0xc5, 0xfa, 0xd3, 0xaa, 0x3a, 0xf6, 0xea, 0x44, 0xc1, 0x18, 0x69, 0xdc, 0x4f, 0x85, 0x3f, 0x00, 0x2b, 0x2e,
0xea, 0x00, 0x00, 0x00, 0x00, 0x77, 0xb2, 0x06, 0xa0, 0x2c, 0xa5, 0xb1, 0xd4, 0xce, 0x6b, 0xbf, 0xdf, 0x0a, 0xca, 0xea, 0x00, 0x00, 0x00, 0x00, 0x77, 0xb2, 0x06, 0xa0, 0x2c, 0xa5, 0xb1, 0xd4, 0xce, 0x6b, 0xbf, 0xdf, 0x0a, 0xca,
@ -131,77 +128,57 @@ void printUsage(const char* executable) {
template<bool softAes> template<bool softAes>
void generateAsm(uint32_t nonce) { void generateAsm(uint32_t nonce) {
alignas(16) uint64_t hash[8]; /*alignas(16) uint64_t hash[8];
uint8_t blockTemplate[sizeof(blockTemplate__)]; uint8_t blockTemplate[sizeof(blockTemplate_)];
memcpy(blockTemplate, blockTemplate__, sizeof(blockTemplate)); memcpy(blockTemplate, blockTemplate_, sizeof(blockTemplate));
store32(blockTemplate + 39, nonce); store32(blockTemplate + 39, nonce);
blake2b(hash, sizeof(hash), blockTemplate, sizeof(blockTemplate), nullptr, 0); blake2b(hash, sizeof(hash), blockTemplate, sizeof(blockTemplate), nullptr, 0);
uint8_t scratchpad[RANDOMX_SCRATCHPAD_L3]; uint8_t scratchpad[RANDOMX_SCRATCHPAD_L3];
fillAes1Rx4<softAes>((void*)hash, RANDOMX_SCRATCHPAD_L3, scratchpad); fillAes1Rx4<softAes>((void*)hash, RANDOMX_SCRATCHPAD_L3, scratchpad);
RandomX::AssemblyGeneratorX86 asmX86; randomx::AssemblyGeneratorX86 asmX86;
RandomX::Program p; randomx::Program p;
fillAes1Rx4<softAes>(hash, sizeof(p), &p); fillAes1Rx4<softAes>(hash, sizeof(p), &p);
asmX86.generateProgram(p); asmX86.generateProgram(p);
asmX86.printCode(std::cout); asmX86.printCode(std::cout);*/
} }
template<bool softAes> template<bool softAes>
void generateNative(uint32_t nonce) { void generateNative(uint32_t nonce) {
alignas(16) uint64_t hash[8]; /*alignas(16) uint64_t hash[8];
uint8_t blockTemplate[sizeof(blockTemplate__)]; uint8_t blockTemplate[sizeof(blockTemplate_)];
memcpy(blockTemplate, blockTemplate__, sizeof(blockTemplate)); memcpy(blockTemplate, blockTemplate_, sizeof(blockTemplate));
store32(blockTemplate + 39, nonce); store32(blockTemplate + 39, nonce);
blake2b(hash, sizeof(hash), blockTemplate, sizeof(blockTemplate), nullptr, 0); blake2b(hash, sizeof(hash), blockTemplate, sizeof(blockTemplate), nullptr, 0);
uint8_t scratchpad[RANDOMX_SCRATCHPAD_L3]; uint8_t scratchpad[RANDOMX_SCRATCHPAD_L3];
fillAes1Rx4<softAes>((void*)hash, RANDOMX_SCRATCHPAD_L3, scratchpad); fillAes1Rx4<softAes>((void*)hash, RANDOMX_SCRATCHPAD_L3, scratchpad);
alignas(16) RandomX::Program prog; alignas(16) randomx::Program prog;
fillAes1Rx4<softAes>((void*)hash, sizeof(prog), &prog); fillAes1Rx4<softAes>((void*)hash, sizeof(prog), &prog);
for (int i = 0; i < RANDOMX_PROGRAM_SIZE; ++i) { for (int i = 0; i < RANDOMX_PROGRAM_SIZE; ++i) {
prog(i).dst %= 8; prog(i).dst %= 8;
prog(i).src %= 8; prog(i).src %= 8;
} }
std::cout << prog << std::endl; std::cout << prog << std::endl;*/
} }
template<bool softAes> void mine(randomx_vm* vm, std::atomic<uint32_t>& atomicNonce, AtomicHash& result, uint32_t noncesCount, int thread) {
void mine(RandomX::VirtualMachine* vm, std::atomic<uint32_t>& atomicNonce, AtomicHash& result, uint32_t noncesCount, int thread, uint8_t* scratchpad) { uint64_t hash[RANDOMX_HASH_SIZE / 4];
alignas(16) uint64_t hash[8]; uint8_t blockTemplate[sizeof(blockTemplate_)];
uint8_t blockTemplate[sizeof(blockTemplate__)]; memcpy(blockTemplate, blockTemplate_, sizeof(blockTemplate));
memcpy(blockTemplate, blockTemplate__, sizeof(blockTemplate));
void* noncePtr = blockTemplate + 39; void* noncePtr = blockTemplate + 39;
auto nonce = atomicNonce.fetch_add(1); auto nonce = atomicNonce.fetch_add(1);
while (nonce < noncesCount) { while (nonce < noncesCount) {
//std::cout << "Thread " << thread << " nonce " << nonce << std::endl; //std::cout << "Thread " << thread << " nonce " << nonce << std::endl;
store32(noncePtr, nonce); store32(noncePtr, nonce);
blake2b(hash, sizeof(hash), blockTemplate, sizeof(blockTemplate), nullptr, 0);
fillAes1Rx4<softAes>((void*)hash, RANDOMX_SCRATCHPAD_L3, scratchpad); randomx_calculate_hash(vm, blockTemplate, sizeof(blockTemplate), &hash);
//dump((char*)scratchpad, RANDOMX_SCRATCHPAD_L3, "spad-before.txt");
vm->resetRoundingMode();
vm->setScratchpad(scratchpad);
for (int chain = 0; chain < RANDOMX_PROGRAM_COUNT - 1; ++chain) {
fillAes1Rx4<softAes>((void*)hash, sizeof(RandomX::Program), vm->getProgramBuffer());
vm->initialize();
vm->execute();
vm->getResult<false>(nullptr, 0, hash);
}
fillAes1Rx4<softAes>((void*)hash, sizeof(RandomX::Program), vm->getProgramBuffer());
vm->initialize();
vm->execute();
/*if (RandomX::trace) {
for (int j = 0; j < RandomX::ProgramLength; ++j) {
uint64_t res = *(uint64_t*)(scratchpad + 8 * (RandomX::ProgramLength - 1 - j));
std::cout << std::hex << std::setw(16) << std::setfill('0') << res << std::endl;
}
}*/
vm->getResult<softAes>(scratchpad, RANDOMX_SCRATCHPAD_L3, hash);
//dump((char*)scratchpad, RANDOMX_SCRATCHPAD_L3, "spad-after.txt");
result.xorWith(hash); result.xorWith(hash);
if (RandomX::trace) { /*if (randomx::trace) {
std::cout << "Nonce: " << nonce << " "; std::cout << "Nonce: " << nonce << " ";
outputHex(std::cout, (char*)hash, 16); outputHex(std::cout, (char*)hash, 16);
std::cout << std::endl; std::cout << std::endl;
} }*/
nonce = atomicNonce.fetch_add(1); nonce = atomicNonce.fetch_add(1);
} }
} }
@ -227,16 +204,16 @@ int main(int argc, char** argv) {
readOption("--genSuperscalar", argc, argv, genSuperscalar); readOption("--genSuperscalar", argc, argv, genSuperscalar);
readOption("--legacy", argc, argv, legacy); readOption("--legacy", argc, argv, legacy);
if (genSuperscalar) { /*if (genSuperscalar) {
RandomX::SuperscalarProgram p; randomx::SuperscalarProgram p;
RandomX::Blake2Generator gen(seed, programCount); randomx::Blake2Generator gen(seed, programCount);
RandomX::generateSuperscalar(p, gen); randomx::generateSuperscalar(p, gen);
RandomX::AssemblyGeneratorX86 asmX86; randomx::AssemblyGeneratorX86 asmX86;
asmX86.generateAsm(p); asmX86.generateAsm(p);
//std::ofstream file("lightProg2.asm"); //std::ofstream file("lightProg2.asm");
asmX86.printCode(std::cout); asmX86.printCode(std::cout);
return 0; return 0;
} }*/
if (genAsm) { if (genAsm) {
if (softAes) if (softAes)
@ -264,15 +241,42 @@ int main(int argc, char** argv) {
std::atomic<uint32_t> atomicNonce(0); std::atomic<uint32_t> atomicNonce(0);
AtomicHash result; AtomicHash result;
std::vector<RandomX::VirtualMachine*> vms; std::vector<randomx_vm*> vms;
std::vector<std::thread> threads; std::vector<std::thread> threads;
RandomX::dataset_t dataset; randomx_dataset* dataset;
const uint64_t cacheSize = (RANDOMX_ARGON_MEMORY + RANDOMX_ARGON_GROWTH * epoch) * RandomX::ArgonBlockSize; randomx_cache* cache;
const uint64_t datasetSize = (RANDOMX_DATASET_SIZE + RANDOMX_DS_GROWTH * epoch); randomx_flags flags = RANDOMX_FLAG_DEFAULT;
dataset.cache.size = cacheSize;
RandomX::SuperscalarProgram programs[RANDOMX_CACHE_ACCESSES];
std::cout << "RandomX - " << (miningMode ? "mining" : "verification") << " mode" << std::endl; if (miningMode) {
flags = (randomx_flags)(flags | RANDOMX_FLAG_FULL_MEM);
std::cout << "RandomX - full memory mode (2 GiB)" << std::endl;
} else {
std::cout << "RandomX - light memory mode (256 MiB)" << std::endl;
}
if (jit) {
flags = (randomx_flags)(flags | RANDOMX_FLAG_JIT);
std::cout << "RandomX - JIT compiled mode" << std::endl;
}
else {
std::cout << "RandomX - interpreted mode" << std::endl;
}
if (softAes) {
std::cout << "RandomX - software AES mode" << std::endl;
}
else {
flags = (randomx_flags)(flags | RANDOMX_FLAG_HARD_AES);
std::cout << "RandomX - hardware AES mode" << std::endl;
}
if (largePages) {
flags = (randomx_flags)(flags | RANDOMX_FLAG_LARGE_PAGES);
std::cout << "RandomX - large pages mode" << std::endl;
}
else {
std::cout << "RandomX - small pages mode" << std::endl;
}
std::cout << "Initializing"; std::cout << "Initializing";
if(miningMode) if(miningMode)
@ -281,39 +285,22 @@ int main(int argc, char** argv) {
try { try {
Stopwatch sw(true); Stopwatch sw(true);
RandomX::datasetInitCache(seed, dataset, largePages); cache = randomx_alloc_cache(flags);
if (RandomX::trace) { randomx_init_cache(cache, seed, sizeof(seed));
/*if (randomx::trace) {
std::cout << "Cache: " << std::endl; std::cout << "Cache: " << std::endl;
outputHex(std::cout, (char*)dataset.cache.memory, sizeof(__m128i)); outputHex(std::cout, (char*)dataset.cache.memory, sizeof(__m128i));
std::cout << std::endl; std::cout << std::endl;
} }*/
if (!legacy) { if (miningMode) {
RandomX::Blake2Generator gen(seed, programCount); dataset = randomx_alloc_dataset(flags);
for (int i = 0; i < RANDOMX_CACHE_ACCESSES; ++i) {
RandomX::generateSuperscalar(programs[i], gen);
}
}
if (!miningMode) {
std::cout << "Cache (" << cacheSize << " bytes) initialized in " << sw.getElapsed() << " s" << std::endl;
}
else {
auto cache = dataset.cache;
dataset.dataset.size = datasetSize;
RandomX::datasetAlloc(dataset, largePages);
const uint64_t datasetBlockCount = datasetSize / RandomX::CacheLineSize;
if (!legacy) {
RandomX::JitCompilerX86 jit86;
jit86.generateSuperScalarHash(programs);
RandomX::DatasetInitFunc dsfunc = jit86.getDatasetInitFunc();
if (initThreadCount > 1) { if (initThreadCount > 1) {
auto perThread = datasetBlockCount / initThreadCount; auto perThread = RANDOMX_DATASET_BLOCKS / initThreadCount;
auto remainder = datasetBlockCount % initThreadCount; auto remainder = RANDOMX_DATASET_BLOCKS % initThreadCount;
uint32_t startBlock = 0; uint32_t startBlock = 0;
uint32_t endBlock = 0;
for (int i = 0; i < initThreadCount; ++i) { for (int i = 0; i < initThreadCount; ++i) {
auto count = perThread + (i == initThreadCount - 1 ? remainder : 0); auto count = perThread + (i == initThreadCount - 1 ? remainder : 0);
endBlock += count; threads.push_back(std::thread(&randomx_init_dataset, dataset, cache, startBlock, count));
threads.push_back(std::thread(dsfunc, cache.memory, dataset.dataset.memory + startBlock * RandomX::CacheLineSize, startBlock, endBlock));
startBlock += count; startBlock += count;
} }
for (unsigned i = 0; i < threads.size(); ++i) { for (unsigned i = 0; i < threads.size(); ++i) {
@ -321,76 +308,37 @@ int main(int argc, char** argv) {
} }
} }
else { else {
dsfunc(cache.memory, dataset.dataset.memory, 0, datasetBlockCount); randomx_init_dataset(dataset, cache, 0, RANDOMX_DATASET_BLOCKS);
} }
//dump((const char*)dataset.dataset.memory, RANDOMX_DATASET_SIZE, "dataset.dat"); //dump((const char*)dataset.dataset.memory, RANDOMX_DATASET_SIZE, "dataset.dat");
} randomx_release_cache(cache);
else {
if (initThreadCount > 1) {
auto perThread = datasetBlockCount / initThreadCount;
auto remainder = datasetBlockCount % initThreadCount;
for (int i = 0; i < initThreadCount; ++i) {
auto count = perThread + (i == initThreadCount - 1 ? remainder : 0);
threads.push_back(std::thread(&RandomX::datasetInit, std::ref(cache), std::ref(dataset.dataset), i * perThread, count));
}
for (unsigned i = 0; i < threads.size(); ++i) {
threads[i].join();
}
}
else {
RandomX::datasetInit(cache, dataset.dataset, 0, datasetBlockCount);
}
}
RandomX::deallocCache(cache, largePages);
threads.clear(); threads.clear();
std::cout << "Dataset (" << datasetSize << " bytes) initialized in " << sw.getElapsed() << " s" << std::endl;
} }
std::cout << "Memory initialized in " << sw.getElapsed() << " s" << std::endl;
std::cout << "Initializing " << threadCount << " virtual machine(s) ..." << std::endl; std::cout << "Initializing " << threadCount << " virtual machine(s) ..." << std::endl;
for (int i = 0; i < threadCount; ++i) { for (int i = 0; i < threadCount; ++i) {
RandomX::VirtualMachine* vm; randomx_vm *vm = randomx_create_vm(flags);
if (miningMode) { if (miningMode)
vm = new RandomX::CompiledVirtualMachine(); randomx_vm_set_dataset(vm, dataset);
}
else {
if (jit && !legacy)
vm = new RandomX::CompiledLightVirtualMachine<true>();
else if (jit)
vm = new RandomX::CompiledLightVirtualMachine<false>();
else if (!legacy)
vm = new RandomX::InterpretedVirtualMachine<true>(softAes);
else else
vm = new RandomX::InterpretedVirtualMachine<false>(softAes); randomx_vm_set_cache(vm, cache);
}
vm->setDataset(dataset, datasetSize, programs);
vms.push_back(vm); vms.push_back(vm);
} }
uint8_t* scratchpadMem;
if (largePages) {
scratchpadMem = (uint8_t*)allocLargePagesMemory(threadCount * RANDOMX_SCRATCHPAD_L3);
}
else {
scratchpadMem = (uint8_t*)_mm_malloc(threadCount * RANDOMX_SCRATCHPAD_L3, RandomX::CacheLineSize);
}
std::cout << "Running benchmark (" << programCount << " nonces) ..." << std::endl; std::cout << "Running benchmark (" << programCount << " nonces) ..." << std::endl;
sw.restart(); sw.restart();
if (threadCount > 1) { if (threadCount > 1) {
for (unsigned i = 0; i < vms.size(); ++i) { for (unsigned i = 0; i < vms.size(); ++i) {
if (softAes) if (softAes)
threads.push_back(std::thread(&mine<true>, vms[i], std::ref(atomicNonce), std::ref(result), programCount, i, scratchpadMem + RANDOMX_SCRATCHPAD_L3 * i)); threads.push_back(std::thread(&mine, vms[i], std::ref(atomicNonce), std::ref(result), programCount, i));
else else
threads.push_back(std::thread(&mine<false>, vms[i], std::ref(atomicNonce), std::ref(result), programCount, i, scratchpadMem + RANDOMX_SCRATCHPAD_L3 * i)); threads.push_back(std::thread(&mine, vms[i], std::ref(atomicNonce), std::ref(result), programCount, i));
} }
for (unsigned i = 0; i < threads.size(); ++i) { for (unsigned i = 0; i < threads.size(); ++i) {
threads[i].join(); threads[i].join();
} }
} }
else { else {
if(softAes) mine(vms[0], std::ref(atomicNonce), std::ref(result), programCount, 0);
mine<true>(vms[0], std::ref(atomicNonce), std::ref(result), programCount, 0, scratchpadMem);
else
mine<false>(vms[0], std::ref(atomicNonce), std::ref(result), programCount, 0, scratchpadMem);
/*if (miningMode)
std::cout << "Average program size: " << ((RandomX::CompiledVirtualMachine*)vms[0])->getTotalSize() / programCount / RandomX::ChainLength << std::endl;*/
} }
double elapsed = sw.getElapsed(); double elapsed = sw.getElapsed();
std::cout << "Calculated result: "; std::cout << "Calculated result: ";

209
src/randomx.cpp Normal file
View File

@ -0,0 +1,209 @@
/*
Copyright (c) 2019 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/>.
*/
#include "randomx.h"
#include "dataset.hpp"
#include "VirtualMachine.hpp"
#include "./InterpretedVirtualMachine.hpp"
#include "./InterpretedLightVirtualMachine.hpp"
#include "./CompiledVirtualMachine.hpp"
#include "./CompiledLightVirtualMachine.hpp"
#include "virtualMemory.hpp"
#include "blake2/blake2.h"
extern "C" {
randomx_cache *randomx_alloc_cache(randomx_flags flags) {
randomx_cache *cache;
switch (flags & (RANDOMX_FLAG_JIT | RANDOMX_FLAG_LARGE_PAGES))
{
case RANDOMX_FLAG_DEFAULT:
cache = new randomx::CacheDefault();
break;
case RANDOMX_FLAG_JIT:
cache = new randomx::CacheWithJitDefault();
break;
case RANDOMX_FLAG_LARGE_PAGES:
cache = new randomx::CacheLargePage();
break;
case RANDOMX_FLAG_JIT | RANDOMX_FLAG_LARGE_PAGES:
cache = new randomx::CacheWithJitLargePage();
break;
default:
UNREACHABLE;
}
if (!cache->allocate()) {
delete cache;
cache = nullptr;
}
return cache;
}
void randomx_init_cache(randomx_cache *cache, const void *seed, size_t seedSize) {
cache->initialize(seed, seedSize);
}
void randomx_release_cache(randomx_cache* cache) {
delete cache;
}
randomx_dataset *randomx_alloc_dataset(randomx_flags flags) {
randomx_dataset *dataset;
if (flags & RANDOMX_FLAG_LARGE_PAGES) {
dataset = new randomx::DatasetLargePage();
}
else {
dataset = new randomx::DatasetDefault();
}
if (!dataset->allocate()) {
delete dataset;
dataset = nullptr;
}
return dataset;
}
void randomx_init_dataset(randomx_dataset *dataset, randomx_cache *cache, unsigned long startBlock, unsigned long blockCount) {
randomx::DatasetInitFunc dsfunc = cache->getInitFunc();
dsfunc(cache, dataset->memory + startBlock * randomx::CacheLineSize, startBlock, startBlock + blockCount);
}
void randomx_release_dataset(randomx_dataset *dataset) {
delete dataset;
}
randomx_vm *randomx_create_vm(randomx_flags flags) {
randomx_vm *vm;
switch (flags & (RANDOMX_FLAG_FULL_MEM | RANDOMX_FLAG_JIT | RANDOMX_FLAG_HARD_AES | RANDOMX_FLAG_LARGE_PAGES)) {
case RANDOMX_FLAG_DEFAULT: //0
vm = new randomx::InterpretedLightVmDefault();
break;
case RANDOMX_FLAG_FULL_MEM: //1
vm = new randomx::InterpretedVmDefault();
break;
case RANDOMX_FLAG_JIT: //2
vm = new randomx::CompiledLightVmDefault();
break;
case RANDOMX_FLAG_FULL_MEM | RANDOMX_FLAG_JIT: //3
vm = new randomx::CompiledVmDefault();
break;
case RANDOMX_FLAG_HARD_AES: //4
vm = new randomx::InterpretedLightVmHardAes();
break;
case RANDOMX_FLAG_FULL_MEM | RANDOMX_FLAG_HARD_AES: //5
vm = new randomx::InterpretedVmHardAes();
break;
case RANDOMX_FLAG_JIT | RANDOMX_FLAG_HARD_AES: //6
vm = new randomx::CompiledLightVmHardAes();
break;
case RANDOMX_FLAG_FULL_MEM | RANDOMX_FLAG_JIT | RANDOMX_FLAG_HARD_AES: //7
vm = new randomx::CompiledVmHardAes();
break;
case RANDOMX_FLAG_LARGE_PAGES: //8
vm = new randomx::InterpretedLightVmLargePage();
break;
case RANDOMX_FLAG_FULL_MEM | RANDOMX_FLAG_LARGE_PAGES: //9
vm = new randomx::InterpretedVmLargePage();
break;
case RANDOMX_FLAG_JIT | RANDOMX_FLAG_LARGE_PAGES: //10
vm = new randomx::CompiledLightVmLargePage();
break;
case RANDOMX_FLAG_FULL_MEM | RANDOMX_FLAG_JIT | RANDOMX_FLAG_LARGE_PAGES: //11
vm = new randomx::CompiledVmLargePage();
break;
case RANDOMX_FLAG_HARD_AES | RANDOMX_FLAG_LARGE_PAGES: //12
vm = new randomx::InterpretedLightVmLargePageHardAes();
break;
case RANDOMX_FLAG_FULL_MEM | RANDOMX_FLAG_HARD_AES | RANDOMX_FLAG_LARGE_PAGES: //13
vm = new randomx::InterpretedVmLargePageHardAes();
break;
case RANDOMX_FLAG_JIT | RANDOMX_FLAG_HARD_AES | RANDOMX_FLAG_LARGE_PAGES: //14
vm = new randomx::CompiledLightVmLargePageHardAes();
break;
case RANDOMX_FLAG_FULL_MEM | RANDOMX_FLAG_JIT | RANDOMX_FLAG_HARD_AES | RANDOMX_FLAG_LARGE_PAGES: //15
vm = new randomx::CompiledVmLargePageHardAes();
break;
default:
UNREACHABLE;
}
if (!vm->allocate()) {
delete vm;
vm = nullptr;
}
return vm;
}
void randomx_vm_set_cache(randomx_vm *machine, randomx_cache* cache) {
machine->setCache(cache);
}
void randomx_vm_set_dataset(randomx_vm *machine, randomx_dataset *dataset) {
machine->setDataset(dataset);
}
void randomx_destroy_vm(randomx_vm *machine) {
delete machine;
}
void randomx_calculate_hash(randomx_vm *machine, void *input, size_t inputSize, void *output) {
alignas(16) uint64_t hash[8];
blake2b(hash, sizeof(hash), input, inputSize, nullptr, 0);
machine->generate(&hash, machine->scratchpad, randomx::ScratchpadSize);
//fillAes1Rx4<false>((void*)hash, RANDOMX_SCRATCHPAD_L3, machine->scratchpad);
//dump((char*)scratchpad, RANDOMX_SCRATCHPAD_L3, "spad-before.txt");
machine->resetRoundingMode();
for (int chain = 0; chain < RANDOMX_PROGRAM_COUNT - 1; ++chain) {
machine->generate(&hash, &machine->program, sizeof(randomx::Program));
//fillAes1Rx4<softAes>((void*)hash, sizeof(RandomX::Program), vm->getProgramBuffer());
machine->initialize();
machine->execute();
blake2b(hash, sizeof(hash), &machine->reg, sizeof(machine->reg), nullptr, 0);
}
machine->generate((void*)hash, &machine->program, sizeof(randomx::Program));
//fillAes1Rx4<softAes>((void*)hash, sizeof(RandomX::Program), vm->getProgramBuffer());
machine->initialize();
machine->execute();
machine->getFinalResult(output, 64);
}
}

130
src/randomx.h Normal file
View File

@ -0,0 +1,130 @@
/*
Copyright (c) 2019 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/>.
*/
#ifndef RANDOMX_H
#define RANDOMX_H
/*
Minimal usage example:
----------------------
#include "randomx.h"
#include <stdio.h>
int main() {
const char mySeed[] = "RandomX example seed";
const char myInput[] = "RandomX example input";
char hash[RANDOMX_HASH_SIZE];
randomx_cache *myCache = randomx_alloc_cache(RANDOMX_FLAG_DEFAULT);
randomx_init_cache(myCache, mySeed, sizeof mySeed);
randomx_vm *myMachine = randomx_create_vm(RANDOMX_FLAG_DEFAULT);
randomx_vm_set_cache(myMachine, myCache);
randomx_calculate_hash(myMachine, myInput, sizeof myInput, hash);
randomx_destroy_vm(myMachine);
randomx_release_cache(myCache);
for (unsigned i = 0; i < RANDOMX_HASH_SIZE; ++i)
printf("%02x", hash[i]);
printf("\n");
return 0;
}
Optimized usage example:
------------------------
#include "randomx.h"
#include <stdio.h>
int main() {
const char mySeed[] = "RandomX example seed";
const char myInput[] = "RandomX example input";
char hash[RANDOMX_HASH_SIZE];
randomx_cache *myCache = randomx_alloc_cache(RANDOMX_FLAG_JIT | RANDOMX_FLAG_LARGE_PAGES);
randomx_init_cache(myCache, mySeed, sizeof mySeed);
randomx_dataset *myDataset = randomx_alloc_dataset(RANDOMX_FLAG_LARGE_PAGES);
randomx_init_dataset(myDataset, myCache, 0, RANDOMX_DATASET_BLOCKS);
randomx_release_cache(myCache);
randomx_vm *myMachine = randomx_create_vm(RANDOMX_FLAG_FULL_MEM | RANDOMX_FLAG_JIT | RANDOMX_FLAG_HARD_AES | RANDOMX_FLAG_LARGE_PAGES);
randomx_vm_set_dataset(myMachine, myDataset);
randomx_calculate_hash(myMachine, myInput, sizeof myInput, hash);
randomx_destroy_vm(myMachine);
randomx_release_dataset(myDataset);
for (unsigned i = 0; i < RANDOMX_HASH_SIZE; ++i)
printf("%02x", hash[i]);
printf("\n");
return 0;
}
*/
#include <stddef.h>
#define RANDOMX_HASH_SIZE 32
#define RANDOMX_DATASET_BLOCKS 33554432UL
typedef enum {
RANDOMX_FLAG_DEFAULT = 0,
RANDOMX_FLAG_FULL_MEM = 1,
RANDOMX_FLAG_JIT = 2,
RANDOMX_FLAG_HARD_AES = 4,
RANDOMX_FLAG_LARGE_PAGES = 8,
} randomx_flags;
typedef struct randomx_dataset randomx_dataset;
typedef struct randomx_cache randomx_cache;
typedef struct randomx_vm randomx_vm;
#if defined(__cplusplus)
extern "C" {
#endif
randomx_cache *randomx_alloc_cache(randomx_flags flags);
void randomx_init_cache(randomx_cache *cache, const void *seed, size_t seedSize);
void randomx_release_cache(randomx_cache* cache);
randomx_dataset *randomx_alloc_dataset(randomx_flags flags);
void randomx_init_dataset(randomx_dataset *dataset, randomx_cache *cache, unsigned long startBlock, unsigned long blockCount);
void randomx_release_dataset(randomx_dataset *dataset);
randomx_vm *randomx_create_vm(randomx_flags flags);
void randomx_vm_set_cache(randomx_vm *machine, randomx_cache* cache);
void randomx_vm_set_dataset(randomx_vm *machine, randomx_dataset *dataset);
void randomx_destroy_vm(randomx_vm *machine);
void randomx_calculate_hash(randomx_vm *machine, void *input, size_t inputSize, void *output);
#if defined(__cplusplus)
}
#endif
#endif

View File

@ -26,8 +26,10 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
#include <stdexcept> #include <stdexcept>
#include <iomanip> #include <iomanip>
#include "superscalarGenerator.hpp" #include "superscalarGenerator.hpp"
#include "intrinPortable.h"
#include "reciprocal.h"
namespace RandomX { namespace randomx {
static bool isMultiplication(int type) { static bool isMultiplication(int type) {
return type == SuperscalarInstructionType::IMUL_R || type == SuperscalarInstructionType::IMULH_R || type == SuperscalarInstructionType::ISMULH_R || type == SuperscalarInstructionType::IMUL_RCP; return type == SuperscalarInstructionType::IMUL_R || type == SuperscalarInstructionType::IMULH_R || type == SuperscalarInstructionType::ISMULH_R || type == SuperscalarInstructionType::IMUL_RCP;
@ -842,4 +844,52 @@ namespace RandomX {
std::cout << std::endl; std::cout << std::endl;
}*/ }*/
} }
void executeSuperscalar(int_reg_t(&r)[8], SuperscalarProgram& prog, std::vector<uint64_t> *reciprocals) {
for (unsigned j = 0; j < prog.getSize(); ++j) {
Instruction& instr = prog(j);
switch (instr.opcode)
{
case randomx::SuperscalarInstructionType::ISUB_R:
r[instr.dst] -= r[instr.src];
break;
case randomx::SuperscalarInstructionType::IXOR_R:
r[instr.dst] ^= r[instr.src];
break;
case randomx::SuperscalarInstructionType::IADD_RS:
r[instr.dst] += r[instr.src] << instr.getModShift2();
break;
case randomx::SuperscalarInstructionType::IMUL_R:
r[instr.dst] *= r[instr.src];
break;
case randomx::SuperscalarInstructionType::IROR_C:
r[instr.dst] = rotr(r[instr.dst], instr.getImm32());
break;
case randomx::SuperscalarInstructionType::IADD_C7:
case randomx::SuperscalarInstructionType::IADD_C8:
case randomx::SuperscalarInstructionType::IADD_C9:
r[instr.dst] += signExtend2sCompl(instr.getImm32());
break;
case randomx::SuperscalarInstructionType::IXOR_C7:
case randomx::SuperscalarInstructionType::IXOR_C8:
case randomx::SuperscalarInstructionType::IXOR_C9:
r[instr.dst] ^= signExtend2sCompl(instr.getImm32());
break;
case randomx::SuperscalarInstructionType::IMULH_R:
r[instr.dst] = mulh(r[instr.dst], r[instr.src]);
break;
case randomx::SuperscalarInstructionType::ISMULH_R:
r[instr.dst] = smulh(r[instr.dst], r[instr.src]);
break;
case randomx::SuperscalarInstructionType::IMUL_RCP:
if (reciprocals != nullptr)
r[instr.dst] *= (*reciprocals)[instr.getImm32()];
else
r[instr.dst] *= reciprocal(instr.getImm32());
break;
default:
UNREACHABLE;
}
}
}
} }

View File

@ -18,10 +18,11 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "Program.hpp" #include "superscalar_program.hpp"
#include "Blake2Generator.hpp" #include "Blake2Generator.hpp"
#include <vector>
namespace RandomX { namespace randomx {
// Intel Ivy Bridge reference // Intel Ivy Bridge reference
namespace SuperscalarInstructionType { //uOPs (decode) execution ports latency code size namespace SuperscalarInstructionType { //uOPs (decode) execution ports latency code size
constexpr int ISUB_R = 0; //1 p015 1 3 (sub) constexpr int ISUB_R = 0; //1 p015 1 3 (sub)
@ -44,4 +45,5 @@ namespace RandomX {
} }
void generateSuperscalar(SuperscalarProgram& prog, Blake2Generator& gen); void generateSuperscalar(SuperscalarProgram& prog, Blake2Generator& gen);
void executeSuperscalar(int_reg_t(&r)[8], SuperscalarProgram& prog, std::vector<uint64_t> *reciprocals = nullptr);
} }

View File

@ -0,0 +1,70 @@
/*
Copyright (c) 2019 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/>.
*/
#pragma once
#include <cstdint>
#include "Instruction.hpp"
#include "configuration.h"
namespace randomx {
class SuperscalarProgram {
public:
Instruction& operator()(int pc) {
return programBuffer[pc];
}
friend std::ostream& operator<<(std::ostream& os, const SuperscalarProgram& p) {
p.print(os);
return os;
}
uint32_t getSize() {
return size;
}
void setSize(uint32_t val) {
size = val;
}
int getAddressRegister() {
return addrReg;
}
void setAddressRegister(uint32_t val) {
addrReg = val;
}
double ipc;
int codeSize;
int macroOps;
int decodeCycles;
int cpuLatency;
int asicLatency;
int mulCount;
int cpuLatencies[8];
int asicLatencies[8];
private:
void print(std::ostream& os) const {
for (unsigned i = 0; i < size; ++i) {
auto instr = programBuffer[i];
os << instr;
}
}
Instruction programBuffer[RANDOMX_SUPERSCALAR_MAX_SIZE];
uint32_t size;
int addrReg;
};
}

View File

@ -124,20 +124,22 @@
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\src\allocator.cpp" />
<ClCompile Include="..\src\argon2_core.c" /> <ClCompile Include="..\src\argon2_core.c" />
<ClCompile Include="..\src\argon2_ref.c" /> <ClCompile Include="..\src\argon2_ref.c" />
<ClCompile Include="..\src\AssemblyGeneratorX86.cpp" /> <ClCompile Include="..\src\AssemblyGeneratorX86.cpp" />
<ClCompile Include="..\src\Blake2Generator.cpp" /> <ClCompile Include="..\src\Blake2Generator.cpp" />
<ClCompile Include="..\src\blake2\blake2b.c" /> <ClCompile Include="..\src\blake2\blake2b.c" />
<ClCompile Include="..\src\Cache.cpp" />
<ClCompile Include="..\src\CompiledLightVirtualMachine.cpp" /> <ClCompile Include="..\src\CompiledLightVirtualMachine.cpp" />
<ClCompile Include="..\src\CompiledVirtualMachine.cpp" /> <ClCompile Include="..\src\CompiledVirtualMachine.cpp" />
<ClCompile Include="..\src\dataset.cpp" /> <ClCompile Include="..\src\dataset.cpp" />
<ClCompile Include="..\src\hashAes1Rx4.cpp" /> <ClCompile Include="..\src\hashAes1Rx4.cpp" />
<ClCompile Include="..\src\Instruction.cpp" /> <ClCompile Include="..\src\Instruction.cpp" />
<ClCompile Include="..\src\instructionsPortable.cpp" /> <ClCompile Include="..\src\instructionsPortable.cpp" />
<ClCompile Include="..\src\InterpretedLightVirtualMachine.cpp" />
<ClCompile Include="..\src\InterpretedVirtualMachine.cpp" /> <ClCompile Include="..\src\InterpretedVirtualMachine.cpp" />
<ClCompile Include="..\src\JitCompilerX86.cpp" /> <ClCompile Include="..\src\JitCompilerX86.cpp" />
<ClCompile Include="..\src\randomx.cpp" />
<ClCompile Include="..\src\superscalarGenerator.cpp" /> <ClCompile Include="..\src\superscalarGenerator.cpp" />
<ClCompile Include="..\src\main.cpp" /> <ClCompile Include="..\src\main.cpp" />
<ClCompile Include="..\src\reciprocal.c" /> <ClCompile Include="..\src\reciprocal.c" />
@ -150,11 +152,11 @@
<MASM Include="..\src\squareHash.asm" /> <MASM Include="..\src\squareHash.asm" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\src\allocator.hpp" />
<ClInclude Include="..\src\argon2.h" /> <ClInclude Include="..\src\argon2.h" />
<ClInclude Include="..\src\argon2_core.h" /> <ClInclude Include="..\src\argon2_core.h" />
<ClInclude Include="..\src\AssemblyGeneratorX86.hpp" /> <ClInclude Include="..\src\AssemblyGeneratorX86.hpp" />
<ClInclude Include="..\src\Blake2Generator.hpp" /> <ClInclude Include="..\src\Blake2Generator.hpp" />
<ClInclude Include="..\src\Cache.hpp" />
<ClInclude Include="..\src\catch.hpp" /> <ClInclude Include="..\src\catch.hpp" />
<ClInclude Include="..\src\common.hpp" /> <ClInclude Include="..\src\common.hpp" />
<ClInclude Include="..\src\CompiledLightVirtualMachine.hpp" /> <ClInclude Include="..\src\CompiledLightVirtualMachine.hpp" />
@ -164,16 +166,19 @@
<ClInclude Include="..\src\hashAes1Rx4.hpp" /> <ClInclude Include="..\src\hashAes1Rx4.hpp" />
<ClInclude Include="..\src\Instruction.hpp" /> <ClInclude Include="..\src\Instruction.hpp" />
<ClInclude Include="..\src\instructionWeights.hpp" /> <ClInclude Include="..\src\instructionWeights.hpp" />
<ClInclude Include="..\src\InterpretedLightVirtualMachine.hpp" />
<ClInclude Include="..\src\InterpretedVirtualMachine.hpp" /> <ClInclude Include="..\src\InterpretedVirtualMachine.hpp" />
<ClInclude Include="..\src\intrinPortable.h" /> <ClInclude Include="..\src\intrinPortable.h" />
<ClInclude Include="..\src\JitCompilerX86-static.hpp" /> <ClInclude Include="..\src\JitCompilerX86-static.hpp" />
<ClInclude Include="..\src\JitCompilerX86.hpp" /> <ClInclude Include="..\src\JitCompilerX86.hpp" />
<ClInclude Include="..\src\randomx.h" />
<ClInclude Include="..\src\superscalarGenerator.hpp" /> <ClInclude Include="..\src\superscalarGenerator.hpp" />
<ClInclude Include="..\src\Program.hpp" /> <ClInclude Include="..\src\Program.hpp" />
<ClInclude Include="..\src\reciprocal.h" /> <ClInclude Include="..\src\reciprocal.h" />
<ClInclude Include="..\src\softAes.h" /> <ClInclude Include="..\src\softAes.h" />
<ClInclude Include="..\src\squareHash.h" /> <ClInclude Include="..\src\squareHash.h" />
<ClInclude Include="..\src\Stopwatch.hpp" /> <ClInclude Include="..\src\Stopwatch.hpp" />
<ClInclude Include="..\src\superscalar_program.hpp" />
<ClInclude Include="..\src\VirtualMachine.hpp" /> <ClInclude Include="..\src\VirtualMachine.hpp" />
<ClInclude Include="..\src\virtualMemory.hpp" /> <ClInclude Include="..\src\virtualMemory.hpp" />
</ItemGroup> </ItemGroup>

View File

@ -27,9 +27,6 @@
<ClCompile Include="..\src\AssemblyGeneratorX86.cpp"> <ClCompile Include="..\src\AssemblyGeneratorX86.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\src\Cache.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\CompiledLightVirtualMachine.cpp"> <ClCompile Include="..\src\CompiledLightVirtualMachine.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
@ -75,6 +72,15 @@
<ClCompile Include="..\src\superscalarGenerator.cpp"> <ClCompile Include="..\src\superscalarGenerator.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\src\randomx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\allocator.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\InterpretedLightVirtualMachine.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<MASM Include="..\src\JitCompilerX86-static.asm"> <MASM Include="..\src\JitCompilerX86-static.asm">
@ -94,9 +100,6 @@
<ClInclude Include="..\src\AssemblyGeneratorX86.hpp"> <ClInclude Include="..\src\AssemblyGeneratorX86.hpp">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\src\Cache.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\src\catch.hpp"> <ClInclude Include="..\src\catch.hpp">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
@ -163,5 +166,17 @@
<ClInclude Include="..\src\superscalarGenerator.hpp"> <ClInclude Include="..\src\superscalarGenerator.hpp">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\src\randomx.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\src\allocator.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\src\InterpretedLightVirtualMachine.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\src\superscalar_program.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>