mirror of
https://github.com/wownero/RandomX.git
synced 2026-10-19 16:28:47 +02:00
Interpreter with bytecode
Fixed some undefined behavior with signed types Fixed different results on big endian systems Removed unused code files Restored FNEG_R instructions Updated documentation
This commit is contained in:
@@ -19,12 +19,12 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
//#define TRACE
|
||||
#define MAGIC_DIVISION
|
||||
#include "AssemblyGeneratorX86.hpp"
|
||||
#include "Pcg32.hpp"
|
||||
#include "common.hpp"
|
||||
#include "instructions.hpp"
|
||||
#ifdef MAGIC_DIVISION
|
||||
#include "divideByConstantCodegen.h"
|
||||
#endif
|
||||
#include "Program.hpp"
|
||||
|
||||
namespace RandomX {
|
||||
|
||||
@@ -48,17 +48,10 @@ namespace RandomX {
|
||||
static const char* regDatasetAddr = "rdi";
|
||||
static const char* regScratchpadAddr = "rsi";
|
||||
|
||||
void AssemblyGeneratorX86::generateProgram(const void* seed) {
|
||||
void AssemblyGeneratorX86::generateProgram(Program& prog) {
|
||||
asmCode.str(std::string()); //clear
|
||||
Pcg32 gen(seed);
|
||||
for (unsigned i = 0; i < sizeof(RegisterFile) / sizeof(Pcg32::result_type); ++i) {
|
||||
gen();
|
||||
}
|
||||
Instruction instr;
|
||||
for (unsigned i = 0; i < ProgramLength; ++i) {
|
||||
for (unsigned j = 0; j < sizeof(instr) / sizeof(Pcg32::result_type); ++j) {
|
||||
*(((uint32_t*)&instr) + j) = gen();
|
||||
}
|
||||
Instruction& instr = prog(i);
|
||||
instr.src %= RegistersCount;
|
||||
instr.dst %= RegistersCount;
|
||||
generateCode(instr, i);
|
||||
@@ -83,7 +76,7 @@ namespace RandomX {
|
||||
}
|
||||
|
||||
int32_t AssemblyGeneratorX86::genAddressImm(Instruction& instr) {
|
||||
return instr.imm32 & ScratchpadL3Mask;
|
||||
return (int32_t)instr.imm32 & ScratchpadL3Mask;
|
||||
}
|
||||
|
||||
//1 uOP
|
||||
@@ -92,7 +85,7 @@ namespace RandomX {
|
||||
asmCode << "\tadd " << regR[instr.dst] << ", " << regR[instr.src] << std::endl;
|
||||
}
|
||||
else {
|
||||
asmCode << "\tadd " << regR[instr.dst] << ", " << instr.imm32 << std::endl;
|
||||
asmCode << "\tadd " << regR[instr.dst] << ", " << (int32_t)instr.imm32 << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +102,7 @@ namespace RandomX {
|
||||
|
||||
//1 uOP
|
||||
void AssemblyGeneratorX86::h_IADD_RC(Instruction& instr, int i) {
|
||||
asmCode << "\tlea " << regR[instr.dst] << ", [" << regR[instr.dst] << "+" << regR[instr.src] << std::showpos << instr.imm32 << std::noshowpos << "]" << std::endl;
|
||||
asmCode << "\tlea " << regR[instr.dst] << ", [" << regR[instr.dst] << "+" << regR[instr.src] << std::showpos << (int32_t)instr.imm32 << std::noshowpos << "]" << std::endl;
|
||||
}
|
||||
|
||||
//1 uOP
|
||||
@@ -118,7 +111,7 @@ namespace RandomX {
|
||||
asmCode << "\tsub " << regR[instr.dst] << ", " << regR[instr.src] << std::endl;
|
||||
}
|
||||
else {
|
||||
asmCode << "\tsub " << regR[instr.dst] << ", " << instr.imm32 << std::endl;
|
||||
asmCode << "\tsub " << regR[instr.dst] << ", " << (int32_t)instr.imm32 << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +128,7 @@ namespace RandomX {
|
||||
|
||||
//1 uOP
|
||||
void AssemblyGeneratorX86::h_IMUL_9C(Instruction& instr, int i) {
|
||||
asmCode << "\tlea " << regR[instr.dst] << ", [" << regR[instr.dst] << "+" << regR[instr.dst] << "*8" << std::showpos << instr.imm32 << std::noshowpos << "]" << std::endl;
|
||||
asmCode << "\tlea " << regR[instr.dst] << ", [" << regR[instr.dst] << "+" << regR[instr.dst] << "*8" << std::showpos << (int32_t)instr.imm32 << std::noshowpos << "]" << std::endl;
|
||||
}
|
||||
|
||||
//1 uOP
|
||||
@@ -144,7 +137,7 @@ namespace RandomX {
|
||||
asmCode << "\timul " << regR[instr.dst] << ", " << regR[instr.src] << std::endl;
|
||||
}
|
||||
else {
|
||||
asmCode << "\timul " << regR[instr.dst] << ", " << instr.imm32 << std::endl;
|
||||
asmCode << "\timul " << regR[instr.dst] << ", " << (int32_t)instr.imm32 << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,16 +154,9 @@ namespace RandomX {
|
||||
|
||||
//4 uOPs
|
||||
void AssemblyGeneratorX86::h_IMULH_R(Instruction& instr, int i) {
|
||||
if (instr.src != instr.dst) {
|
||||
asmCode << "\tmov rax, " << regR[instr.dst] << std::endl;
|
||||
asmCode << "\tmul " << regR[instr.src] << std::endl;
|
||||
asmCode << "\tmov " << regR[instr.dst] << ", rdx" << std::endl;
|
||||
}
|
||||
else {
|
||||
asmCode << "\tmov eax, " << instr.imm32 << std::endl;
|
||||
asmCode << "\tmul " << regR[instr.dst] << std::endl;
|
||||
asmCode << "\tadd " << regR[instr.dst] << ", rdx" << std::endl;
|
||||
}
|
||||
asmCode << "\tmov rax, " << regR[instr.dst] << std::endl;
|
||||
asmCode << "\tmul " << regR[instr.src] << std::endl;
|
||||
asmCode << "\tmov " << regR[instr.dst] << ", rdx" << std::endl;
|
||||
}
|
||||
|
||||
//5.75 uOPs
|
||||
@@ -189,16 +175,9 @@ namespace RandomX {
|
||||
|
||||
//4 uOPs
|
||||
void AssemblyGeneratorX86::h_ISMULH_R(Instruction& instr, int i) {
|
||||
if (instr.src != instr.dst) {
|
||||
asmCode << "\tmov rax, " << regR[instr.dst] << std::endl;
|
||||
asmCode << "\timul " << regR[instr.src] << std::endl;
|
||||
asmCode << "\tmov " << regR[instr.dst] << ", rdx" << std::endl;
|
||||
}
|
||||
else {
|
||||
asmCode << "\tmov rax, " << instr.imm32 << std::endl;
|
||||
asmCode << "\timul " << regR[instr.dst] << std::endl;
|
||||
asmCode << "\tadd " << regR[instr.dst] << ", rdx" << std::endl;
|
||||
}
|
||||
asmCode << "\tmov rax, " << regR[instr.dst] << std::endl;
|
||||
asmCode << "\timul " << regR[instr.src] << std::endl;
|
||||
asmCode << "\tmov " << regR[instr.dst] << ", rdx" << std::endl;
|
||||
}
|
||||
|
||||
//5.75 uOPs
|
||||
@@ -226,7 +205,7 @@ namespace RandomX {
|
||||
asmCode << "\txor " << regR[instr.dst] << ", " << regR[instr.src] << std::endl;
|
||||
}
|
||||
else {
|
||||
asmCode << "\txor " << regR[instr.dst] << ", " << instr.imm32 << std::endl;
|
||||
asmCode << "\txor " << regR[instr.dst] << ", " << (int32_t)instr.imm32 << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,7 +279,7 @@ namespace RandomX {
|
||||
|
||||
//~8.5 uOPs
|
||||
void AssemblyGeneratorX86::h_ISDIV_C(Instruction& instr, int i) {
|
||||
int64_t divisor = instr.imm32;
|
||||
int64_t divisor = (int32_t)instr.imm32;
|
||||
if ((divisor & -divisor) == divisor || (divisor & -divisor) == -divisor) {
|
||||
asmCode << "\tmov rax, " << regR[instr.dst] << std::endl;
|
||||
// +/- power of two
|
||||
@@ -395,9 +374,9 @@ namespace RandomX {
|
||||
}
|
||||
|
||||
//1 uOP
|
||||
void AssemblyGeneratorX86::h_CFSUM_R(Instruction& instr, int i) {
|
||||
void AssemblyGeneratorX86::h_FNEG_R(Instruction& instr, int i) {
|
||||
instr.dst %= 4;
|
||||
asmCode << "\t" << fsumInstr[instr.mod % 4] << " " << signMask << ", " << regF[instr.dst] << std::endl;
|
||||
asmCode << "\txorps " << regF[instr.dst] << ", " << signMask << std::endl;
|
||||
}
|
||||
|
||||
//1 uOPs
|
||||
@@ -478,7 +457,7 @@ namespace RandomX {
|
||||
//4 uOPs
|
||||
void AssemblyGeneratorX86::h_COND_R(Instruction& instr, int i) {
|
||||
asmCode << "\txor ecx, ecx" << std::endl;
|
||||
asmCode << "\tcmp " << regR32[instr.src] << ", " << instr.imm32 << std::endl;
|
||||
asmCode << "\tcmp " << regR32[instr.src] << ", " << (int32_t)instr.imm32 << std::endl;
|
||||
asmCode << "\tset" << condition(instr) << " cl" << std::endl;
|
||||
asmCode << "\tadd " << regR[instr.dst] << ", rcx" << std::endl;
|
||||
}
|
||||
@@ -487,7 +466,7 @@ namespace RandomX {
|
||||
void AssemblyGeneratorX86::h_COND_M(Instruction& instr, int i) {
|
||||
asmCode << "\txor ecx, ecx" << std::endl;
|
||||
genAddressReg(instr);
|
||||
asmCode << "\tcmp dword ptr [rsi+rax], " << instr.imm32 << std::endl;
|
||||
asmCode << "\tcmp dword ptr [rsi+rax], " << (int32_t)instr.imm32 << std::endl;
|
||||
asmCode << "\tset" << condition(instr) << " cl" << std::endl;
|
||||
asmCode << "\tadd " << regR[instr.dst] << ", rcx" << std::endl;
|
||||
}
|
||||
@@ -542,7 +521,7 @@ namespace RandomX {
|
||||
INST_HANDLE(FADD_M)
|
||||
INST_HANDLE(FSUB_R)
|
||||
INST_HANDLE(FSUB_M)
|
||||
INST_HANDLE(CFSUM_R)
|
||||
INST_HANDLE(FNEG_R)
|
||||
|
||||
//Floating point group E
|
||||
INST_HANDLE(FMUL_R)
|
||||
|
||||
@@ -24,13 +24,14 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace RandomX {
|
||||
|
||||
class Program;
|
||||
class AssemblyGeneratorX86;
|
||||
|
||||
typedef void(AssemblyGeneratorX86::*InstructionGenerator)(Instruction&, int);
|
||||
|
||||
class AssemblyGeneratorX86 {
|
||||
public:
|
||||
void generateProgram(const void* seed);
|
||||
void generateProgram(Program&);
|
||||
void printCode(std::ostream& os) {
|
||||
os << asmCode.rdbuf();
|
||||
}
|
||||
@@ -69,7 +70,7 @@ namespace RandomX {
|
||||
void h_FADD_M(Instruction&, int);
|
||||
void h_FSUB_R(Instruction&, int);
|
||||
void h_FSUB_M(Instruction&, int);
|
||||
void h_CFSUM_R(Instruction&, int);
|
||||
void h_FNEG_R(Instruction&, int);
|
||||
void h_FMUL_R(Instruction&, int);
|
||||
void h_FMUL_M(Instruction&, int);
|
||||
void h_FDIV_R(Instruction&, int);
|
||||
|
||||
@@ -23,7 +23,6 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
#include "Cache.hpp"
|
||||
#include "softAes.h"
|
||||
#include "argon2.h"
|
||||
#include "Pcg32.hpp"
|
||||
#include "argon2_core.h"
|
||||
|
||||
namespace RandomX {
|
||||
|
||||
@@ -18,19 +18,12 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "CompiledVirtualMachine.hpp"
|
||||
#include "Pcg32.hpp"
|
||||
#include "common.hpp"
|
||||
#include "instructions.hpp"
|
||||
#include <stdexcept>
|
||||
|
||||
namespace RandomX {
|
||||
|
||||
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;
|
||||
|
||||
CompiledVirtualMachine::CompiledVirtualMachine() {
|
||||
totalSize = 0;
|
||||
}
|
||||
@@ -39,40 +32,9 @@ namespace RandomX {
|
||||
mem.ds = ds;
|
||||
}
|
||||
|
||||
void CompiledVirtualMachine::initializeScratchpad(uint8_t* scratchpad, int32_t index) {
|
||||
memcpy(scratchpad, mem.ds.dataset + ScratchpadSize * index, ScratchpadSize);
|
||||
}
|
||||
|
||||
static 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 CompiledVirtualMachine::initializeProgram(const void* seed) {
|
||||
Pcg32 gen(seed);
|
||||
for (unsigned i = 0; i < sizeof(reg) / sizeof(Pcg32::result_type); ++i) {
|
||||
*(((uint32_t*)®) + i) = gen();
|
||||
}
|
||||
initFpu();
|
||||
/*for (int i = 0; i < RegistersCount / 2; ++i) {
|
||||
reg.f[i].lo.f64 = (double)reg.f[i].lo.i64;
|
||||
reg.f[i].hi.f64 = (double)reg.f[i].hi.i64;
|
||||
}
|
||||
for (int i = 0; i < RegistersCount / 2; ++i) {
|
||||
reg.g[i].lo.f64 = std::abs((double)reg.g[i].lo.i64);
|
||||
reg.g[i].hi.f64 = std::abs((double)reg.g[i].hi.i64);
|
||||
}*/
|
||||
for (int i = 0; i < RegistersCount / 2; ++i) {
|
||||
reg.a[i].lo.u64 = getSmallPositiveFloatBits(reg.f[i].lo.u64);
|
||||
reg.a[i].hi.u64 = getSmallPositiveFloatBits(reg.f[i].hi.u64);
|
||||
}
|
||||
compiler.generateProgram(gen);
|
||||
mem.ma = (gen() ^ *(((uint32_t*)seed) + 4)) & -64;
|
||||
mem.mx = *(((uint32_t*)seed) + 5);
|
||||
void CompiledVirtualMachine::initialize() {
|
||||
VirtualMachine::initialize();
|
||||
compiler.generateProgram(program);
|
||||
}
|
||||
|
||||
void CompiledVirtualMachine::execute() {
|
||||
|
||||
@@ -39,8 +39,7 @@ namespace RandomX {
|
||||
}
|
||||
CompiledVirtualMachine();
|
||||
void setDataset(dataset_t ds) override;
|
||||
void initializeScratchpad(uint8_t* scratchpad, int32_t index) override;
|
||||
void initializeProgram(const void* seed) override;
|
||||
void initialize() override;
|
||||
virtual void execute() override;
|
||||
void* getProgram() {
|
||||
return compiler.getCode();
|
||||
|
||||
+6
-16
@@ -116,12 +116,7 @@ namespace RandomX {
|
||||
}
|
||||
|
||||
void Instruction::h_IMULH_R(std::ostream& os) const {
|
||||
if (src != dst) {
|
||||
os << "r" << (int)dst << ", r" << (int)src << std::endl;
|
||||
}
|
||||
else {
|
||||
os << "r" << (int)dst << ", " << imm32 << std::endl;
|
||||
}
|
||||
os << "r" << (int)dst << ", r" << (int)src << std::endl;
|
||||
}
|
||||
|
||||
void Instruction::h_IMULH_M(std::ostream& os) const {
|
||||
@@ -138,12 +133,7 @@ namespace RandomX {
|
||||
}
|
||||
|
||||
void Instruction::h_ISMULH_R(std::ostream& os) const {
|
||||
if (src != dst) {
|
||||
os << "r" << (int)dst << ", r" << (int)src << std::endl;
|
||||
}
|
||||
else {
|
||||
os << "r" << (int)dst << ", " << imm32 << std::endl;
|
||||
}
|
||||
os << "r" << (int)dst << ", r" << (int)src << std::endl;
|
||||
}
|
||||
|
||||
void Instruction::h_ISMULH_M(std::ostream& os) const {
|
||||
@@ -247,9 +237,9 @@ namespace RandomX {
|
||||
os << std::endl;
|
||||
}
|
||||
|
||||
void Instruction::h_CFSUM_R(std::ostream& os) const {
|
||||
void Instruction::h_FNEG_R(std::ostream& os) const {
|
||||
auto dstIndex = dst % 4;
|
||||
os << "f" << dstIndex << ", " << (1 << ((mod % 4) + 3)) << std::endl;
|
||||
os << "f" << dstIndex << std::endl;
|
||||
}
|
||||
|
||||
void Instruction::h_FMUL_R(std::ostream& os) const {
|
||||
@@ -370,7 +360,7 @@ namespace RandomX {
|
||||
INST_NAME(FADD_M)
|
||||
INST_NAME(FSUB_R)
|
||||
INST_NAME(FSUB_M)
|
||||
INST_NAME(CFSUM_R)
|
||||
INST_NAME(FNEG_R)
|
||||
|
||||
//Floating point group E
|
||||
INST_NAME(FMUL_R)
|
||||
@@ -421,7 +411,7 @@ namespace RandomX {
|
||||
INST_HANDLE(FADD_M)
|
||||
INST_HANDLE(FSUB_R)
|
||||
INST_HANDLE(FSUB_M)
|
||||
INST_HANDLE(CFSUM_R)
|
||||
INST_HANDLE(FNEG_R)
|
||||
|
||||
//Floating point group E
|
||||
INST_HANDLE(FMUL_R)
|
||||
|
||||
+3
-3
@@ -54,7 +54,7 @@ namespace RandomX {
|
||||
constexpr int FADD_M = 22;
|
||||
constexpr int FSUB_R = 23;
|
||||
constexpr int FSUB_M = 24;
|
||||
constexpr int CFSUM_R = 25;
|
||||
constexpr int FNEG_R = 25;
|
||||
constexpr int FMUL_R = 26;
|
||||
constexpr int FMUL_M = 27;
|
||||
constexpr int FDIV_R = 28;
|
||||
@@ -74,7 +74,7 @@ namespace RandomX {
|
||||
uint8_t dst;
|
||||
uint8_t src;
|
||||
uint8_t mod;
|
||||
int32_t imm32;
|
||||
uint32_t imm32;
|
||||
const char* getName() const {
|
||||
return names[opcode];
|
||||
}
|
||||
@@ -116,7 +116,7 @@ namespace RandomX {
|
||||
void h_FADD_M(std::ostream&) const;
|
||||
void h_FSUB_R(std::ostream&) const;
|
||||
void h_FSUB_M(std::ostream&) const;
|
||||
void h_CFSUM_R(std::ostream&) const;
|
||||
void h_FNEG_R(std::ostream&) const;
|
||||
void h_FMUL_R(std::ostream&) const;
|
||||
void h_FMUL_M(std::ostream&) const;
|
||||
void h_FDIV_R(std::ostream&) const;
|
||||
|
||||
@@ -19,7 +19,6 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
//#define TRACE
|
||||
//#define FPUCHECK
|
||||
#include "InterpretedVirtualMachine.hpp"
|
||||
#include "Pcg32.hpp"
|
||||
#include "instructions.hpp"
|
||||
#include "dataset.hpp"
|
||||
#include "Cache.hpp"
|
||||
@@ -34,6 +33,7 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
#ifdef STATS
|
||||
#include <algorithm>
|
||||
#endif
|
||||
#include "divideByConstantCodegen.h"
|
||||
|
||||
#ifdef FPUCHECK
|
||||
constexpr bool fpuCheck = true;
|
||||
@@ -61,88 +61,683 @@ namespace RandomX {
|
||||
}
|
||||
else {
|
||||
mem.ds = ds;
|
||||
if (softAes) {
|
||||
readDataset = &datasetReadLight<true>;
|
||||
}
|
||||
else {
|
||||
readDataset = &datasetReadLight<false>;
|
||||
}
|
||||
readDataset = &datasetReadLight;
|
||||
}
|
||||
}
|
||||
|
||||
void InterpretedVirtualMachine::initializeScratchpad(uint8_t* scratchpad, int32_t index) {
|
||||
uint32_t startingBlock = (ScratchpadSize / CacheLineSize) * index;
|
||||
if (asyncWorker) {
|
||||
ILightClientAsyncWorker* worker = mem.ds.asyncWorker;
|
||||
const uint32_t blocksPerThread = (ScratchpadSize / CacheLineSize) / 2;
|
||||
worker->prepareBlocks(scratchpad, startingBlock, blocksPerThread); //async first half
|
||||
worker->getBlocks(scratchpad + ScratchpadLength / 2, startingBlock + blocksPerThread, blocksPerThread); //sync second half
|
||||
worker->sync();
|
||||
}
|
||||
else {
|
||||
auto cache = mem.ds.cache;
|
||||
if (softAes) {
|
||||
for (int i = 0; i < ScratchpadSize / CacheLineSize; ++i) {
|
||||
initBlock<true>(cache->getCache(), ((uint8_t*)scratchpad) + CacheLineSize * i, (ScratchpadSize / CacheLineSize) * index + i, cache->getKeys());
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < ScratchpadSize / CacheLineSize; ++i) {
|
||||
initBlock<false>(cache->getCache(), ((uint8_t*)scratchpad) + CacheLineSize * i, (ScratchpadSize / CacheLineSize) * index + i, cache->getKeys());
|
||||
}
|
||||
}
|
||||
void InterpretedVirtualMachine::initialize() {
|
||||
VirtualMachine::initialize();
|
||||
for (unsigned i = 0; i < ProgramLength; ++i) {
|
||||
program(i).src %= RegistersCount;
|
||||
program(i).dst %= RegistersCount;
|
||||
}
|
||||
}
|
||||
|
||||
void InterpretedVirtualMachine::initializeProgram(const void* seed) {
|
||||
Pcg32 gen(seed);
|
||||
for (unsigned i = 0; i < sizeof(reg) / sizeof(Pcg32::result_type); ++i) {
|
||||
*(((uint32_t*)®) + i) = gen();
|
||||
template<int N>
|
||||
void InterpretedVirtualMachine::executeBytecode(int_reg_t(&r)[8], __m128d (&f)[4], __m128d (&e)[4], __m128d (&a)[4]) {
|
||||
executeBytecode(N, r, f, e, a);
|
||||
executeBytecode<N + 1>(r, f, e, a);
|
||||
}
|
||||
|
||||
template<>
|
||||
void InterpretedVirtualMachine::executeBytecode<ProgramLength>(int_reg_t(&r)[8], __m128d (&f)[4], __m128d (&e)[4], __m128d (&a)[4]) {
|
||||
}
|
||||
|
||||
FORCE_INLINE void InterpretedVirtualMachine::executeBytecode(int i, int_reg_t(&r)[8], __m128d (&f)[4], __m128d (&e)[4], __m128d (&a)[4]) {
|
||||
auto& ibc = byteCode[i];
|
||||
switch (ibc.type)
|
||||
{
|
||||
case InstructionType::IADD_R: {
|
||||
*ibc.idst += *ibc.isrc;
|
||||
} break;
|
||||
|
||||
case InstructionType::IADD_M: {
|
||||
*ibc.idst += load64(scratchpad + (*ibc.isrc & ibc.memMask));
|
||||
} break;
|
||||
|
||||
case InstructionType::IADD_RC: {
|
||||
*ibc.idst += *ibc.isrc + ibc.imm;
|
||||
} break;
|
||||
|
||||
case InstructionType::ISUB_R: {
|
||||
*ibc.idst -= *ibc.isrc;
|
||||
} break;
|
||||
|
||||
case InstructionType::ISUB_M: {
|
||||
*ibc.idst -= load64(scratchpad + (*ibc.isrc & ibc.memMask));
|
||||
} break;
|
||||
|
||||
case InstructionType::IMUL_9C: {
|
||||
*ibc.idst += 9 * *ibc.idst + ibc.imm;
|
||||
} break;
|
||||
|
||||
case InstructionType::IMUL_R: {
|
||||
*ibc.idst *= *ibc.isrc;
|
||||
} break;
|
||||
|
||||
case InstructionType::IMUL_M: {
|
||||
*ibc.idst *= load64(scratchpad + (*ibc.isrc & ibc.memMask));
|
||||
} break;
|
||||
|
||||
case InstructionType::IMULH_R: {
|
||||
*ibc.idst = mulh(*ibc.idst, *ibc.isrc);
|
||||
} break;
|
||||
|
||||
case InstructionType::IMULH_M: {
|
||||
*ibc.idst = mulh(*ibc.idst, load64(scratchpad + (*ibc.isrc & ibc.memMask)));
|
||||
} break;
|
||||
|
||||
case InstructionType::ISMULH_R: {
|
||||
*ibc.idst = smulh(unsigned64ToSigned2sCompl(*ibc.idst), unsigned64ToSigned2sCompl(*ibc.isrc));
|
||||
} break;
|
||||
|
||||
case InstructionType::ISMULH_M: {
|
||||
*ibc.idst = smulh(unsigned64ToSigned2sCompl(*ibc.idst), unsigned64ToSigned2sCompl(load64(scratchpad + (*ibc.isrc & ibc.memMask))));
|
||||
} break;
|
||||
|
||||
case InstructionType::IDIV_C: {
|
||||
if (ibc.signedMultiplier != 0) {
|
||||
int_reg_t dividend = *ibc.idst;
|
||||
int_reg_t quotient = dividend >> ibc.preShift;
|
||||
if (ibc.increment) {
|
||||
quotient = quotient == UINT64_MAX ? UINT64_MAX : quotient + 1;
|
||||
}
|
||||
quotient = mulh(quotient, ibc.signedMultiplier);
|
||||
quotient >>= ibc.postShift;
|
||||
*ibc.idst += quotient;
|
||||
}
|
||||
else {
|
||||
*ibc.idst += *ibc.idst >> ibc.shift;
|
||||
}
|
||||
} break;
|
||||
|
||||
case InstructionType::ISDIV_C: {
|
||||
|
||||
} break;
|
||||
|
||||
case InstructionType::INEG_R: {
|
||||
*ibc.idst = ~(*ibc.idst) + 1; //two's complement negative
|
||||
} break;
|
||||
|
||||
case InstructionType::IXOR_R: {
|
||||
*ibc.idst ^= *ibc.isrc;
|
||||
} break;
|
||||
|
||||
case InstructionType::IXOR_M: {
|
||||
*ibc.idst ^= load64(scratchpad + (*ibc.isrc & ibc.memMask));
|
||||
} break;
|
||||
|
||||
case InstructionType::IROR_R: {
|
||||
*ibc.idst = rotr(*ibc.idst, *ibc.isrc & 63);
|
||||
} break;
|
||||
|
||||
case InstructionType::IROL_R: {
|
||||
*ibc.idst = rotl(*ibc.idst, *ibc.isrc & 63);
|
||||
} break;
|
||||
|
||||
case InstructionType::ISWAP_R: {
|
||||
int_reg_t temp = *ibc.isrc;
|
||||
*ibc.isrc = *ibc.idst;
|
||||
*ibc.idst = temp;
|
||||
} break;
|
||||
|
||||
case InstructionType::FSWAP_R: {
|
||||
*ibc.fdst = _mm_shuffle_pd(*ibc.fdst, *ibc.fdst, 1);
|
||||
} break;
|
||||
|
||||
case InstructionType::FADD_R: {
|
||||
*ibc.fdst = _mm_add_pd(*ibc.fdst, *ibc.fsrc);
|
||||
} break;
|
||||
|
||||
case InstructionType::FADD_M: {
|
||||
__m128d fsrc = load_cvt_i32x2(scratchpad + (*ibc.isrc & ibc.memMask));
|
||||
*ibc.fdst = _mm_add_pd(*ibc.fdst, fsrc);
|
||||
} break;
|
||||
|
||||
case InstructionType::FSUB_R: {
|
||||
*ibc.fdst = _mm_sub_pd(*ibc.fdst, *ibc.fsrc);
|
||||
} break;
|
||||
|
||||
case InstructionType::FSUB_M: {
|
||||
__m128d fsrc = load_cvt_i32x2(scratchpad + (*ibc.isrc & ibc.memMask));
|
||||
*ibc.fdst = _mm_sub_pd(*ibc.fdst, fsrc);
|
||||
} break;
|
||||
|
||||
case InstructionType::FNEG_R: {
|
||||
const __m128d signMask = _mm_castsi128_pd(_mm_set1_epi64x(1ULL << 63));
|
||||
*ibc.fdst = _mm_xor_pd(*ibc.fdst, signMask);
|
||||
} break;
|
||||
|
||||
case InstructionType::FMUL_R: {
|
||||
*ibc.fdst = _mm_mul_pd(*ibc.fdst, *ibc.fsrc);
|
||||
} break;
|
||||
|
||||
case InstructionType::FDIV_M: {
|
||||
__m128d fsrc = load_cvt_i32x2(scratchpad + (*ibc.isrc & ibc.memMask));
|
||||
__m128d fdst = _mm_div_pd(*ibc.fdst, fsrc);
|
||||
*ibc.fdst = _mm_max_pd(fdst, _mm_set_pd(DBL_MIN, DBL_MIN));
|
||||
} break;
|
||||
|
||||
case InstructionType::FSQRT_R: {
|
||||
*ibc.fdst = _mm_sqrt_pd(*ibc.fdst);
|
||||
} break;
|
||||
|
||||
case InstructionType::COND_R: {
|
||||
*ibc.idst += condition(*ibc.isrc, ibc.imm, ibc.condition) ? 1 : 0;
|
||||
} break;
|
||||
|
||||
case InstructionType::COND_M: {
|
||||
*ibc.idst += condition(load64(scratchpad + (*ibc.isrc & ibc.memMask)), ibc.imm, ibc.condition) ? 1 : 0;
|
||||
} break;
|
||||
|
||||
case InstructionType::CFROUND: {
|
||||
setRoundMode(rotr(*ibc.isrc, ibc.imm) % 4);
|
||||
} break;
|
||||
|
||||
case InstructionType::ISTORE: {
|
||||
store64(scratchpad + (*ibc.idst & ibc.memMask), *ibc.isrc);
|
||||
} break;
|
||||
|
||||
case InstructionType::NOP: {
|
||||
//nothing
|
||||
} break;
|
||||
|
||||
default:
|
||||
UNREACHABLE;
|
||||
}
|
||||
initFpu();
|
||||
for (int i = 0; i < RegistersCount; ++i) {
|
||||
reg.f[i].lo.f64 = (double)reg.f[i].lo.i64;
|
||||
reg.f[i].hi.f64 = (double)reg.f[i].hi.i64;
|
||||
}
|
||||
//std::cout << reg;
|
||||
p.initialize(gen);
|
||||
currentTransform = addressTransformations[gen.getUniform(0, TransformationCount - 1)];
|
||||
mem.ma = (gen() ^ *(((uint32_t*)seed) + 4)) & ~7;
|
||||
mem.mx = *(((uint32_t*)seed) + 5);
|
||||
pc = 0;
|
||||
ic = InstructionCount;
|
||||
stack.clear();
|
||||
}
|
||||
|
||||
void InterpretedVirtualMachine::execute() {
|
||||
for(int i = 0; i < InstructionCount; ++i) {
|
||||
for (int j = 0; j < ProgramLength; ++j) {
|
||||
auto& ibc = byteCode[j];
|
||||
switch (ibc.type)
|
||||
{
|
||||
case InstructionType::CFROUND: {
|
||||
uint64_t rcFlag = rotr(ibc.isrc->u64, ibc.imm.i32);
|
||||
setRoundMode(rcFlag);
|
||||
}
|
||||
break;
|
||||
}
|
||||
int_reg_t r[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
__m128d f[4];
|
||||
__m128d e[4];
|
||||
__m128d a[4];
|
||||
|
||||
a[0] = _mm_load_pd(®.a[0].lo);
|
||||
a[1] = _mm_load_pd(®.a[1].lo);
|
||||
a[2] = _mm_load_pd(®.a[2].lo);
|
||||
a[3] = _mm_load_pd(®.a[3].lo);
|
||||
|
||||
precompileProgram(r, f, e, a);
|
||||
|
||||
uint32_t spAddr0 = mem.mx;
|
||||
uint32_t spAddr1 = mem.ma;
|
||||
|
||||
for(int iter = 0; iter < InstructionCount; ++iter) {
|
||||
//std::cout << "Iteration " << iter << std::endl;
|
||||
spAddr0 ^= r[readReg0];
|
||||
spAddr0 &= ScratchpadL3Mask64;
|
||||
|
||||
r[0] ^= load64(scratchpad + spAddr0 + 0);
|
||||
r[1] ^= load64(scratchpad + spAddr0 + 8);
|
||||
r[2] ^= load64(scratchpad + spAddr0 + 16);
|
||||
r[3] ^= load64(scratchpad + spAddr0 + 24);
|
||||
r[4] ^= load64(scratchpad + spAddr0 + 32);
|
||||
r[5] ^= load64(scratchpad + spAddr0 + 40);
|
||||
r[6] ^= load64(scratchpad + spAddr0 + 48);
|
||||
r[7] ^= load64(scratchpad + spAddr0 + 56);
|
||||
|
||||
spAddr1 ^= r[readReg1];
|
||||
spAddr1 &= ScratchpadL3Mask64;
|
||||
|
||||
f[0] = load_cvt_i32x2(scratchpad + spAddr1 + 0);
|
||||
f[1] = load_cvt_i32x2(scratchpad + spAddr1 + 8);
|
||||
f[2] = load_cvt_i32x2(scratchpad + spAddr1 + 16);
|
||||
f[3] = load_cvt_i32x2(scratchpad + spAddr1 + 24);
|
||||
e[0] = _mm_abs(load_cvt_i32x2(scratchpad + spAddr1 + 32));
|
||||
e[1] = _mm_abs(load_cvt_i32x2(scratchpad + spAddr1 + 40));
|
||||
e[2] = _mm_abs(load_cvt_i32x2(scratchpad + spAddr1 + 48));
|
||||
e[3] = _mm_abs(load_cvt_i32x2(scratchpad + spAddr1 + 56));
|
||||
|
||||
executeBytecode<0>(r, f, e, a);
|
||||
|
||||
if (asyncWorker) {
|
||||
ILightClientAsyncWorker* aw = mem.ds.asyncWorker;
|
||||
const uint64_t* datasetLine = aw->getBlock(mem.ma);
|
||||
for (int i = 0; i < RegistersCount; ++i)
|
||||
r[i] ^= datasetLine[i];
|
||||
mem.mx ^= r[readReg2] ^ r[readReg3];
|
||||
mem.mx &= CacheLineAlignMask; //align to cache line
|
||||
std::swap(mem.mx, mem.ma);
|
||||
aw->prepareBlock(mem.ma);
|
||||
}
|
||||
else {
|
||||
mem.mx ^= r[readReg2] ^ r[readReg3];
|
||||
mem.mx &= CacheLineAlignMask;
|
||||
Cache* cache = mem.ds.cache;
|
||||
uint64_t datasetLine[CacheLineSize / sizeof(uint64_t)];
|
||||
initBlock(cache->getCache(), (uint8_t*)datasetLine, mem.ma / CacheLineSize, cache->getKeys());
|
||||
for (int i = 0; i < RegistersCount; ++i)
|
||||
r[i] ^= datasetLine[i];
|
||||
std::swap(mem.mx, mem.ma);
|
||||
}
|
||||
|
||||
store64(scratchpad + spAddr1 + 0, r[0]);
|
||||
store64(scratchpad + spAddr1 + 8, r[1]);
|
||||
store64(scratchpad + spAddr1 + 16, r[2]);
|
||||
store64(scratchpad + spAddr1 + 24, r[3]);
|
||||
store64(scratchpad + spAddr1 + 32, r[4]);
|
||||
store64(scratchpad + spAddr1 + 40, r[5]);
|
||||
store64(scratchpad + spAddr1 + 48, r[6]);
|
||||
store64(scratchpad + spAddr1 + 56, r[7]);
|
||||
|
||||
_mm_store_pd((double*)(scratchpad + spAddr0 + 0), _mm_mul_pd(f[0], e[0]));
|
||||
_mm_store_pd((double*)(scratchpad + spAddr0 + 16), _mm_mul_pd(f[1], e[1]));
|
||||
_mm_store_pd((double*)(scratchpad + spAddr0 + 32), _mm_mul_pd(f[2], e[2]));
|
||||
_mm_store_pd((double*)(scratchpad + spAddr0 + 48), _mm_mul_pd(f[3], e[3]));
|
||||
|
||||
spAddr0 = 0;
|
||||
spAddr1 = 0;
|
||||
}
|
||||
|
||||
store64(®.r[0], r[0]);
|
||||
store64(®.r[1], r[1]);
|
||||
store64(®.r[2], r[2]);
|
||||
store64(®.r[3], r[3]);
|
||||
store64(®.r[4], r[4]);
|
||||
store64(®.r[5], r[5]);
|
||||
store64(®.r[6], r[6]);
|
||||
store64(®.r[7], r[7]);
|
||||
|
||||
_mm_store_pd(®.f[0].lo, f[0]);
|
||||
_mm_store_pd(®.f[1].lo, f[1]);
|
||||
_mm_store_pd(®.f[2].lo, f[2]);
|
||||
_mm_store_pd(®.f[3].lo, f[3]);
|
||||
_mm_store_pd(®.e[0].lo, e[0]);
|
||||
_mm_store_pd(®.e[1].lo, e[1]);
|
||||
_mm_store_pd(®.e[2].lo, e[2]);
|
||||
_mm_store_pd(®.e[3].lo, e[3]);
|
||||
}
|
||||
|
||||
#include "instructionWeights.hpp"
|
||||
|
||||
void InterpretedVirtualMachine::executeInstruction(Instruction& instr) {
|
||||
switch (instr.opcode)
|
||||
{
|
||||
CASE_REP(IADD_R)
|
||||
void InterpretedVirtualMachine::precompileProgram(int_reg_t(&r)[8], __m128d (&f)[4], __m128d (&e)[4], __m128d (&a)[4]) {
|
||||
for (unsigned i = 0; i < ProgramLength; ++i) {
|
||||
auto& instr = program(i);
|
||||
auto& ibc = byteCode[i];
|
||||
switch (instr.opcode) {
|
||||
CASE_REP(IADD_R) {
|
||||
auto dst = instr.dst % RegistersCount;
|
||||
auto src = instr.src % RegistersCount;
|
||||
ibc.type = InstructionType::IADD_R;
|
||||
ibc.idst = &r[dst];
|
||||
if (src != dst) {
|
||||
ibc.isrc = &r[src];
|
||||
}
|
||||
else {
|
||||
ibc.imm = signExtend2sCompl(instr.imm32);
|
||||
ibc.isrc = &ibc.imm;
|
||||
}
|
||||
} break;
|
||||
|
||||
break;
|
||||
CASE_REP(IADD_M) {
|
||||
auto dst = instr.dst % RegistersCount;
|
||||
auto src = instr.src % RegistersCount;
|
||||
ibc.type = InstructionType::IADD_M;
|
||||
ibc.idst = &r[dst];
|
||||
if (instr.src != instr.dst) {
|
||||
ibc.isrc = &r[src];
|
||||
ibc.memMask = ((instr.mod % 4) ? ScratchpadL1Mask : ScratchpadL2Mask);
|
||||
}
|
||||
else {
|
||||
ibc.imm = instr.imm32;
|
||||
ibc.isrc = &ibc.imm;
|
||||
ibc.memMask = ScratchpadL3Mask;
|
||||
}
|
||||
} break;
|
||||
|
||||
CASE_REP(IADD_RC) {
|
||||
auto dst = instr.dst % RegistersCount;
|
||||
auto src = instr.src % RegistersCount;
|
||||
ibc.type = InstructionType::IADD_RC;
|
||||
ibc.idst = &r[dst];
|
||||
ibc.isrc = &r[src];
|
||||
ibc.imm = signExtend2sCompl(instr.imm32);
|
||||
} break;
|
||||
|
||||
CASE_REP(ISUB_R) {
|
||||
auto dst = instr.dst % RegistersCount;
|
||||
auto src = instr.src % RegistersCount;
|
||||
ibc.type = InstructionType::ISUB_R;
|
||||
ibc.idst = &r[dst];
|
||||
if (src != dst) {
|
||||
ibc.isrc = &r[src];
|
||||
}
|
||||
else {
|
||||
ibc.imm = signExtend2sCompl(instr.imm32);
|
||||
ibc.isrc = &ibc.imm;
|
||||
}
|
||||
} break;
|
||||
|
||||
CASE_REP(ISUB_M) {
|
||||
auto dst = instr.dst % RegistersCount;
|
||||
auto src = instr.src % RegistersCount;
|
||||
ibc.type = InstructionType::ISUB_M;
|
||||
ibc.idst = &r[dst];
|
||||
if (instr.src != instr.dst) {
|
||||
ibc.isrc = &r[src];
|
||||
ibc.memMask = ((instr.mod % 4) ? ScratchpadL1Mask : ScratchpadL2Mask);
|
||||
}
|
||||
else {
|
||||
ibc.imm = instr.imm32;
|
||||
ibc.isrc = &ibc.imm;
|
||||
ibc.memMask = ScratchpadL3Mask;
|
||||
}
|
||||
} break;
|
||||
|
||||
CASE_REP(IMUL_9C) {
|
||||
auto dst = instr.dst % RegistersCount;
|
||||
ibc.type = InstructionType::IMUL_9C;
|
||||
ibc.idst = &r[dst];
|
||||
ibc.imm = signExtend2sCompl(instr.imm32);
|
||||
} break;
|
||||
|
||||
CASE_REP(IMUL_R) {
|
||||
auto dst = instr.dst % RegistersCount;
|
||||
auto src = instr.src % RegistersCount;
|
||||
ibc.type = InstructionType::IMUL_R;
|
||||
ibc.idst = &r[dst];
|
||||
if (src != dst) {
|
||||
ibc.isrc = &r[src];
|
||||
}
|
||||
else {
|
||||
ibc.imm = signExtend2sCompl(instr.imm32);
|
||||
ibc.isrc = &ibc.imm;
|
||||
}
|
||||
} break;
|
||||
|
||||
CASE_REP(IMUL_M) {
|
||||
auto dst = instr.dst % RegistersCount;
|
||||
auto src = instr.src % RegistersCount;
|
||||
ibc.type = InstructionType::IMUL_M;
|
||||
ibc.idst = &r[dst];
|
||||
if (instr.src != instr.dst) {
|
||||
ibc.isrc = &r[src];
|
||||
ibc.memMask = ((instr.mod % 4) ? ScratchpadL1Mask : ScratchpadL2Mask);
|
||||
}
|
||||
else {
|
||||
ibc.imm = instr.imm32;
|
||||
ibc.isrc = &ibc.imm;
|
||||
ibc.memMask = ScratchpadL3Mask;
|
||||
}
|
||||
} break;
|
||||
|
||||
CASE_REP(IMULH_R) {
|
||||
auto dst = instr.dst % RegistersCount;
|
||||
auto src = instr.src % RegistersCount;
|
||||
ibc.type = InstructionType::IMULH_R;
|
||||
ibc.idst = &r[dst];
|
||||
ibc.isrc = &r[src];
|
||||
} break;
|
||||
|
||||
CASE_REP(IMULH_M) {
|
||||
auto dst = instr.dst % RegistersCount;
|
||||
auto src = instr.src % RegistersCount;
|
||||
ibc.type = InstructionType::IMULH_M;
|
||||
ibc.idst = &r[dst];
|
||||
if (instr.src != instr.dst) {
|
||||
ibc.isrc = &r[src];
|
||||
ibc.memMask = ((instr.mod % 4) ? ScratchpadL1Mask : ScratchpadL2Mask);
|
||||
}
|
||||
else {
|
||||
ibc.imm = instr.imm32;
|
||||
ibc.isrc = &ibc.imm;
|
||||
ibc.memMask = ScratchpadL3Mask;
|
||||
}
|
||||
} break;
|
||||
|
||||
CASE_REP(ISMULH_R) {
|
||||
auto dst = instr.dst % RegistersCount;
|
||||
auto src = instr.src % RegistersCount;
|
||||
ibc.type = InstructionType::ISMULH_R;
|
||||
ibc.idst = &r[dst];
|
||||
ibc.isrc = &r[src];
|
||||
} break;
|
||||
|
||||
CASE_REP(ISMULH_M) {
|
||||
auto dst = instr.dst % RegistersCount;
|
||||
auto src = instr.src % RegistersCount;
|
||||
ibc.type = InstructionType::ISMULH_M;
|
||||
ibc.idst = &r[dst];
|
||||
if (instr.src != instr.dst) {
|
||||
ibc.isrc = &r[src];
|
||||
ibc.memMask = ((instr.mod % 4) ? ScratchpadL1Mask : ScratchpadL2Mask);
|
||||
}
|
||||
else {
|
||||
ibc.imm = instr.imm32;
|
||||
ibc.isrc = &ibc.imm;
|
||||
ibc.memMask = ScratchpadL3Mask;
|
||||
}
|
||||
} break;
|
||||
|
||||
CASE_REP(IDIV_C) {
|
||||
uint32_t divisor = instr.imm32;
|
||||
if (divisor != 0) {
|
||||
auto dst = instr.dst % RegistersCount;
|
||||
ibc.type = InstructionType::IDIV_C;
|
||||
ibc.idst = &r[dst];
|
||||
if (divisor & (divisor - 1)) {
|
||||
magicu_info mi = compute_unsigned_magic_info(divisor, sizeof(uint64_t) * 8);
|
||||
ibc.signedMultiplier = mi.multiplier;
|
||||
ibc.preShift = mi.pre_shift;
|
||||
ibc.postShift = mi.post_shift;
|
||||
ibc.increment = mi.increment;
|
||||
}
|
||||
else {
|
||||
ibc.signedMultiplier = 0;
|
||||
int shift = 0;
|
||||
while (divisor >>= 1)
|
||||
++shift;
|
||||
ibc.shift = shift;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ibc.type = InstructionType::NOP;
|
||||
}
|
||||
} break;
|
||||
|
||||
CASE_REP(ISDIV_C) {
|
||||
ibc.type = InstructionType::NOP;
|
||||
} break;
|
||||
|
||||
CASE_REP(INEG_R) {
|
||||
auto dst = instr.dst % RegistersCount;
|
||||
ibc.type = InstructionType::INEG_R;
|
||||
ibc.idst = &r[dst];
|
||||
} break;
|
||||
|
||||
CASE_REP(IXOR_R) {
|
||||
auto dst = instr.dst % RegistersCount;
|
||||
auto src = instr.src % RegistersCount;
|
||||
ibc.type = InstructionType::IXOR_R;
|
||||
ibc.idst = &r[dst];
|
||||
if (src != dst) {
|
||||
ibc.isrc = &r[src];
|
||||
}
|
||||
else {
|
||||
ibc.imm = signExtend2sCompl(instr.imm32);
|
||||
ibc.isrc = &ibc.imm;
|
||||
}
|
||||
} break;
|
||||
|
||||
CASE_REP(IXOR_M) {
|
||||
auto dst = instr.dst % RegistersCount;
|
||||
auto src = instr.src % RegistersCount;
|
||||
ibc.type = InstructionType::IXOR_M;
|
||||
ibc.idst = &r[dst];
|
||||
if (instr.src != instr.dst) {
|
||||
ibc.isrc = &r[src];
|
||||
ibc.memMask = ((instr.mod % 4) ? ScratchpadL1Mask : ScratchpadL2Mask);
|
||||
}
|
||||
else {
|
||||
ibc.imm = instr.imm32;
|
||||
ibc.isrc = &ibc.imm;
|
||||
ibc.memMask = ScratchpadL3Mask;
|
||||
}
|
||||
} break;
|
||||
|
||||
CASE_REP(IROR_R) {
|
||||
auto dst = instr.dst % RegistersCount;
|
||||
auto src = instr.src % RegistersCount;
|
||||
ibc.type = InstructionType::IROR_R;
|
||||
ibc.idst = &r[dst];
|
||||
if (src != dst) {
|
||||
ibc.isrc = &r[src];
|
||||
}
|
||||
else {
|
||||
ibc.imm = instr.imm32;
|
||||
ibc.isrc = &ibc.imm;
|
||||
}
|
||||
} break;
|
||||
|
||||
CASE_REP(IROL_R) {
|
||||
auto dst = instr.dst % RegistersCount;
|
||||
auto src = instr.src % RegistersCount;
|
||||
ibc.type = InstructionType::IROL_R;
|
||||
ibc.idst = &r[dst];
|
||||
if (src != dst) {
|
||||
ibc.isrc = &r[src];
|
||||
}
|
||||
else {
|
||||
ibc.imm = instr.imm32;
|
||||
ibc.isrc = &ibc.imm;
|
||||
}
|
||||
} break;
|
||||
|
||||
CASE_REP(ISWAP_R) {
|
||||
auto dst = instr.dst % RegistersCount;
|
||||
auto src = instr.src % RegistersCount;
|
||||
if (src != dst) {
|
||||
ibc.idst = &r[dst];
|
||||
ibc.isrc = &r[src];
|
||||
ibc.type = InstructionType::ISWAP_R;
|
||||
}
|
||||
else {
|
||||
ibc.type = InstructionType::NOP;
|
||||
}
|
||||
} break;
|
||||
|
||||
CASE_REP(FSWAP_R) {
|
||||
auto dst = instr.dst % RegistersCount;
|
||||
ibc.type = InstructionType::FSWAP_R;
|
||||
ibc.fdst = &f[dst];
|
||||
} break;
|
||||
|
||||
CASE_REP(FADD_R) {
|
||||
auto dst = instr.dst % 4;
|
||||
auto src = instr.src % 4;
|
||||
ibc.type = InstructionType::FADD_R;
|
||||
ibc.fdst = &f[dst];
|
||||
ibc.fsrc = &a[src];
|
||||
} break;
|
||||
|
||||
CASE_REP(FADD_M) {
|
||||
auto dst = instr.dst % 4;
|
||||
auto src = instr.src % 8;
|
||||
ibc.type = InstructionType::FADD_M;
|
||||
ibc.fdst = &f[dst];
|
||||
ibc.isrc = &r[src];
|
||||
ibc.memMask = ((instr.mod % 4) ? ScratchpadL1Mask : ScratchpadL2Mask);
|
||||
} break;
|
||||
|
||||
CASE_REP(FSUB_R) {
|
||||
auto dst = instr.dst % 4;
|
||||
auto src = instr.src % 4;
|
||||
ibc.type = InstructionType::FSUB_R;
|
||||
ibc.fdst = &f[dst];
|
||||
ibc.fsrc = &a[src];
|
||||
} break;
|
||||
|
||||
CASE_REP(FSUB_M) {
|
||||
auto dst = instr.dst % 4;
|
||||
auto src = instr.src % 8;
|
||||
ibc.type = InstructionType::FSUB_M;
|
||||
ibc.fdst = &f[dst];
|
||||
ibc.isrc = &r[src];
|
||||
ibc.memMask = ((instr.mod % 4) ? ScratchpadL1Mask : ScratchpadL2Mask);
|
||||
} break;
|
||||
|
||||
CASE_REP(FNEG_R) {
|
||||
auto dst = instr.dst % 4;
|
||||
ibc.fdst = &f[dst];
|
||||
ibc.type = InstructionType::FNEG_R;
|
||||
} break;
|
||||
|
||||
CASE_REP(FMUL_R) {
|
||||
auto dst = instr.dst % 4;
|
||||
auto src = instr.src % 4;
|
||||
ibc.type = InstructionType::FMUL_R;
|
||||
ibc.fdst = &e[dst];
|
||||
ibc.fsrc = &a[src];
|
||||
} break;
|
||||
|
||||
CASE_REP(FMUL_M) {
|
||||
} break;
|
||||
|
||||
CASE_REP(FDIV_R) {
|
||||
} break;
|
||||
|
||||
CASE_REP(FDIV_M) {
|
||||
auto dst = instr.dst % 4;
|
||||
auto src = instr.src % 8;
|
||||
ibc.type = InstructionType::FDIV_M;
|
||||
ibc.fdst = &e[dst];
|
||||
ibc.isrc = &r[src];
|
||||
ibc.memMask = ((instr.mod % 4) ? ScratchpadL1Mask : ScratchpadL2Mask);
|
||||
} break;
|
||||
|
||||
CASE_REP(FSQRT_R) {
|
||||
auto dst = instr.dst % 4;
|
||||
ibc.type = InstructionType::FSQRT_R;
|
||||
ibc.fdst = &e[dst];
|
||||
} break;
|
||||
|
||||
CASE_REP(COND_R) {
|
||||
auto dst = instr.dst % RegistersCount;
|
||||
auto src = instr.src % RegistersCount;
|
||||
ibc.type = InstructionType::COND_R;
|
||||
ibc.idst = &r[dst];
|
||||
ibc.isrc = &r[src];
|
||||
ibc.condition = (instr.mod >> 2) & 7;
|
||||
ibc.imm = instr.imm32;
|
||||
} break;
|
||||
|
||||
CASE_REP(COND_M) {
|
||||
auto dst = instr.dst % RegistersCount;
|
||||
auto src = instr.src % RegistersCount;
|
||||
ibc.type = InstructionType::COND_M;
|
||||
ibc.idst = &r[dst];
|
||||
ibc.isrc = &r[src];
|
||||
ibc.condition = (instr.mod >> 2) & 7;
|
||||
ibc.imm = instr.imm32;
|
||||
ibc.memMask = ((instr.mod % 4) ? ScratchpadL1Mask : ScratchpadL2Mask);
|
||||
} break;
|
||||
|
||||
CASE_REP(CFROUND) {
|
||||
auto src = instr.src % 8;
|
||||
ibc.isrc = &r[src];
|
||||
ibc.type = InstructionType::CFROUND;
|
||||
ibc.imm = instr.imm32 & 63;
|
||||
} break;
|
||||
|
||||
CASE_REP(ISTORE) {
|
||||
auto dst = instr.dst % RegistersCount;
|
||||
auto src = instr.src % RegistersCount;
|
||||
ibc.type = InstructionType::ISTORE;
|
||||
ibc.idst = &r[dst];
|
||||
ibc.isrc = &r[src];
|
||||
} break;
|
||||
|
||||
CASE_REP(FSTORE) {
|
||||
} break;
|
||||
|
||||
CASE_REP(NOP) {
|
||||
ibc.type = InstructionType::NOP;
|
||||
} break;
|
||||
|
||||
default:
|
||||
UNREACHABLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InstructionHandler InterpretedVirtualMachine::engine[256] = {
|
||||
|
||||
};
|
||||
}
|
||||
@@ -21,7 +21,7 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
//#define STATS
|
||||
#include "VirtualMachine.hpp"
|
||||
#include "Program.hpp"
|
||||
#include <vector>
|
||||
#include "intrinPortable.h"
|
||||
|
||||
namespace RandomX {
|
||||
|
||||
@@ -38,15 +38,23 @@ namespace RandomX {
|
||||
|
||||
typedef void(InterpretedVirtualMachine::*InstructionHandler)(Instruction&);
|
||||
|
||||
struct alignas(64) InstructionByteCode {
|
||||
convertible_t* idst;
|
||||
convertible_t* isrc;
|
||||
convertible_t imm;
|
||||
fpu_reg_t* fdst;
|
||||
fpu_reg_t* fsrc;
|
||||
struct alignas(16) InstructionByteCode {
|
||||
int_reg_t* idst;
|
||||
int_reg_t* isrc;
|
||||
int_reg_t imm;
|
||||
__m128d* fdst;
|
||||
__m128d* fsrc;
|
||||
uint32_t condition;
|
||||
uint32_t memMask;
|
||||
uint32_t type;
|
||||
union {
|
||||
uint64_t unsignedMultiplier;
|
||||
int64_t signedMultiplier;
|
||||
};
|
||||
unsigned shift;
|
||||
unsigned preShift;
|
||||
unsigned postShift;
|
||||
bool increment;
|
||||
};
|
||||
|
||||
constexpr int asedwfagdewsa = sizeof(InstructionByteCode);
|
||||
@@ -56,21 +64,14 @@ namespace RandomX {
|
||||
InterpretedVirtualMachine(bool soft, bool async) : softAes(soft), asyncWorker(async) {}
|
||||
~InterpretedVirtualMachine();
|
||||
void setDataset(dataset_t ds) override;
|
||||
void initializeScratchpad(uint8_t* scratchpad, int32_t index) override;
|
||||
void initializeProgram(const void* seed) override;
|
||||
void initialize() override;
|
||||
void execute() override;
|
||||
const Program& getProgam() {
|
||||
return p;
|
||||
}
|
||||
private:
|
||||
static InstructionHandler engine[256];
|
||||
static const ITransform* addressTransformations[TransformationCount];
|
||||
DatasetReadFunc readDataset;
|
||||
bool softAes, asyncWorker;
|
||||
Program p;
|
||||
InstructionByteCode byteCode[ProgramLength];
|
||||
std::vector<convertible_t> stack;
|
||||
uint64_t pc, ic;
|
||||
const ITransform* currentTransform;
|
||||
|
||||
#ifdef STATS
|
||||
int count_ADD_64 = 0;
|
||||
int count_ADD_32 = 0;
|
||||
@@ -121,66 +122,9 @@ namespace RandomX {
|
||||
int count_FMUL_nop2 = 0;
|
||||
int datasetAccess[256] = { 0 };
|
||||
#endif
|
||||
void executeInstruction(Instruction&);
|
||||
convertible_t loada(Instruction&);
|
||||
convertible_t loadbiashift(Instruction&);
|
||||
convertible_t loadbiadiv(Instruction&);
|
||||
convertible_t loadbia(Instruction&);
|
||||
convertible_t& getcr(Instruction&);
|
||||
void writecf(Instruction&, fpu_reg_t&);
|
||||
|
||||
void stackPush(convertible_t& c) {
|
||||
stack.push_back(c);
|
||||
}
|
||||
|
||||
void stackPush(uint64_t x) {
|
||||
convertible_t c;
|
||||
c.u64 = x;
|
||||
stack.push_back(c);
|
||||
}
|
||||
|
||||
convertible_t stackPopValue() {
|
||||
convertible_t top = stack.back();
|
||||
stack.pop_back();
|
||||
return top;
|
||||
}
|
||||
|
||||
uint64_t stackPopAddress() {
|
||||
convertible_t top = stack.back();
|
||||
stack.pop_back();
|
||||
return top.u64;
|
||||
}
|
||||
|
||||
void h_ADD_64(Instruction&);
|
||||
void h_ADD_32(Instruction&);
|
||||
void h_SUB_64(Instruction&);
|
||||
void h_SUB_32(Instruction&);
|
||||
void h_MUL_64(Instruction&);
|
||||
void h_MULH_64(Instruction&);
|
||||
void h_MUL_32(Instruction&);
|
||||
void h_IMUL_32(Instruction&);
|
||||
void h_IMULH_64(Instruction&);
|
||||
void h_DIV_64(Instruction&);
|
||||
void h_IDIV_64(Instruction&);
|
||||
void h_AND_64(Instruction&);
|
||||
void h_AND_32(Instruction&);
|
||||
void h_OR_64(Instruction&);
|
||||
void h_OR_32(Instruction&);
|
||||
void h_XOR_64(Instruction&);
|
||||
void h_XOR_32(Instruction&);
|
||||
void h_SHL_64(Instruction&);
|
||||
void h_SHR_64(Instruction&);
|
||||
void h_SAR_64(Instruction&);
|
||||
void h_ROL_64(Instruction&);
|
||||
void h_ROR_64(Instruction&);
|
||||
void h_FADD(Instruction&);
|
||||
void h_FSUB(Instruction&);
|
||||
void h_FMUL(Instruction&);
|
||||
void h_FDIV(Instruction&);
|
||||
void h_FSQRT(Instruction&);
|
||||
void h_FPROUND(Instruction&);
|
||||
void h_JUMP(Instruction&);
|
||||
void h_CALL(Instruction&);
|
||||
void h_RET(Instruction&);
|
||||
void precompileProgram(int_reg_t(&r)[8], __m128d (&f)[4], __m128d (&e)[4], __m128d (&a)[4]);
|
||||
template<int N>
|
||||
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]);
|
||||
};
|
||||
}
|
||||
+28
-51
@@ -19,7 +19,7 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
|
||||
#define MAGIC_DIVISION
|
||||
#include "JitCompilerX86.hpp"
|
||||
#include "Pcg32.hpp"
|
||||
#include "Program.hpp"
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#ifdef MAGIC_DIVISION
|
||||
@@ -43,7 +43,7 @@ namespace RandomX {
|
||||
//throw std::runtime_error("JIT compiler only supports x86-64 CPUs");
|
||||
}
|
||||
|
||||
void JitCompilerX86::generateProgram(Pcg32& gen) {
|
||||
void JitCompilerX86::generateProgram(Program& p) {
|
||||
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace RandomX {
|
||||
; xmm12 -> temporary
|
||||
; xmm13 -> DBL_MIN
|
||||
; xmm14 -> absolute value mask 0x7fffffffffffffff7fffffffffffffff
|
||||
; xmm15 -> unused
|
||||
; xmm15 -> sign mask 0x80000000000000008000000000000000
|
||||
|
||||
*/
|
||||
|
||||
@@ -199,35 +199,32 @@ namespace RandomX {
|
||||
memcpy(code + CodeSize - epilogueSize, codeEpilogue, epilogueSize);
|
||||
}
|
||||
|
||||
void JitCompilerX86::generateProgram(Pcg32& gen) {
|
||||
auto addressRegisters = gen();
|
||||
int readReg1 = addressRegisters & 1;
|
||||
void JitCompilerX86::generateProgram(Program& prog) {
|
||||
auto addressRegisters = prog.getEntropy(12);
|
||||
uint32_t readReg0 = 0 + (addressRegisters & 1);
|
||||
addressRegisters >>= 1;
|
||||
int readReg2 = 2 + (addressRegisters & 1);
|
||||
uint32_t readReg1 = 2 + (addressRegisters & 1);
|
||||
addressRegisters >>= 1;
|
||||
int readReg3 = 4 + (addressRegisters & 1);
|
||||
uint32_t readReg2 = 4 + (addressRegisters & 1);
|
||||
addressRegisters >>= 1;
|
||||
int readReg4 = 6 + (addressRegisters & 1);
|
||||
uint32_t readReg3 = 6 + (addressRegisters & 1);
|
||||
codePos = prologueSize;
|
||||
emit(REX_XOR_RAX_R64);
|
||||
emitByte(0xc0 + readReg1);
|
||||
emitByte(0xc0 + readReg0);
|
||||
emit(REX_XOR_RAX_R64);
|
||||
emitByte(0xc0 + readReg2);
|
||||
emitByte(0xc0 + readReg1);
|
||||
memcpy(code + codePos, codeLoopLoad, loopLoadSize);
|
||||
codePos += loopLoadSize;
|
||||
Instruction instr;
|
||||
for (unsigned i = 0; i < ProgramLength; ++i) {
|
||||
for (unsigned j = 0; j < sizeof(instr) / sizeof(Pcg32::result_type); ++j) {
|
||||
*(((uint32_t*)&instr) + j) = gen();
|
||||
}
|
||||
Instruction& instr = prog(i);
|
||||
instr.src %= RegistersCount;
|
||||
instr.dst %= RegistersCount;
|
||||
generateCode(instr);
|
||||
}
|
||||
emit(REX_MOV_RR);
|
||||
emitByte(0xc0 + readReg3);
|
||||
emitByte(0xc0 + readReg2);
|
||||
emit(REX_XOR_EAX);
|
||||
emitByte(0xc0 + readReg4);
|
||||
emitByte(0xc0 + readReg3);
|
||||
memcpy(code + codePos, codeReadDataset, readDatasetSize);
|
||||
codePos += readDatasetSize;
|
||||
memcpy(code + codePos, codeLoopStore, loopStoreSize);
|
||||
@@ -365,22 +362,12 @@ namespace RandomX {
|
||||
}
|
||||
|
||||
void JitCompilerX86::h_IMULH_R(Instruction& instr) {
|
||||
if (instr.src != instr.dst) {
|
||||
emit(REX_MOV_RR64);
|
||||
emitByte(0xc0 + instr.dst);
|
||||
emit(REX_MUL_R);
|
||||
emitByte(0xe0 + instr.src);
|
||||
emit(REX_MOV_R64R);
|
||||
emitByte(0xc2 + 8 * instr.dst);
|
||||
}
|
||||
else {
|
||||
emitByte(MOV_EAX_I);
|
||||
emit32(instr.imm32);
|
||||
emit(REX_MUL_R);
|
||||
emitByte(0xe0 + instr.dst);
|
||||
emit(REX_ADD_RM);
|
||||
emitByte(0xc2 + 8 * instr.dst);
|
||||
}
|
||||
emit(REX_MOV_RR64);
|
||||
emitByte(0xc0 + instr.dst);
|
||||
emit(REX_MUL_R);
|
||||
emitByte(0xe0 + instr.src);
|
||||
emit(REX_MOV_R64R);
|
||||
emitByte(0xc2 + 8 * instr.dst);
|
||||
}
|
||||
|
||||
void JitCompilerX86::h_IMULH_M(Instruction& instr) {
|
||||
@@ -402,22 +389,12 @@ namespace RandomX {
|
||||
}
|
||||
|
||||
void JitCompilerX86::h_ISMULH_R(Instruction& instr) {
|
||||
if (instr.src != instr.dst) {
|
||||
emit(REX_MOV_RR64);
|
||||
emitByte(0xc0 + instr.dst);
|
||||
emit(REX_MUL_R);
|
||||
emitByte(0xe8 + instr.src);
|
||||
emit(REX_MOV_R64R);
|
||||
emitByte(0xc2 + 8 * instr.dst);
|
||||
}
|
||||
else {
|
||||
emitByte(MOV_EAX_I);
|
||||
emit32(instr.imm32);
|
||||
emit(REX_MUL_R);
|
||||
emitByte(0xe8 + instr.dst);
|
||||
emit(REX_ADD_RM);
|
||||
emitByte(0xc2 + 8 * instr.dst);
|
||||
}
|
||||
emit(REX_MOV_RR64);
|
||||
emitByte(0xc0 + instr.dst);
|
||||
emit(REX_MUL_R);
|
||||
emitByte(0xe8 + instr.src);
|
||||
emit(REX_MOV_R64R);
|
||||
emitByte(0xc2 + 8 * instr.dst);
|
||||
}
|
||||
|
||||
void JitCompilerX86::h_ISMULH_M(Instruction& instr) {
|
||||
@@ -648,7 +625,7 @@ namespace RandomX {
|
||||
emitByte(0xc4 + 8 * instr.dst);
|
||||
}
|
||||
|
||||
void JitCompilerX86::h_CFSUM_R(Instruction& instr) {
|
||||
void JitCompilerX86::h_FNEG_R(Instruction& instr) {
|
||||
instr.dst %= 4;
|
||||
emit(REX_XORPS);
|
||||
emitByte(0xc7 + 8 * instr.dst);
|
||||
@@ -802,7 +779,7 @@ namespace RandomX {
|
||||
INST_HANDLE(FADD_M)
|
||||
INST_HANDLE(FSUB_R)
|
||||
INST_HANDLE(FSUB_M)
|
||||
INST_HANDLE(CFSUM_R)
|
||||
INST_HANDLE(FNEG_R)
|
||||
INST_HANDLE(FMUL_R)
|
||||
INST_HANDLE(FMUL_M)
|
||||
INST_HANDLE(FDIV_R)
|
||||
|
||||
@@ -24,10 +24,9 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
class Pcg32;
|
||||
|
||||
namespace RandomX {
|
||||
|
||||
class Program;
|
||||
class JitCompilerX86;
|
||||
|
||||
typedef void(JitCompilerX86::*InstructionGeneratorX86)(Instruction&);
|
||||
@@ -37,7 +36,7 @@ namespace RandomX {
|
||||
class JitCompilerX86 {
|
||||
public:
|
||||
JitCompilerX86();
|
||||
void generateProgram(Pcg32&);
|
||||
void generateProgram(Program&);
|
||||
ProgramFunc getProgramFunc() {
|
||||
return (ProgramFunc)code;
|
||||
}
|
||||
@@ -115,7 +114,7 @@ namespace RandomX {
|
||||
void h_FADD_M(Instruction&);
|
||||
void h_FSUB_R(Instruction&);
|
||||
void h_FSUB_M(Instruction&);
|
||||
void h_CFSUM_R(Instruction&);
|
||||
void h_FNEG_R(Instruction&);
|
||||
void h_FMUL_R(Instruction&);
|
||||
void h_FMUL_M(Instruction&);
|
||||
void h_FDIV_R(Instruction&);
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace RandomX {
|
||||
template<bool softAes>
|
||||
void LightClientAsyncWorker<softAes>::prepareBlock(addr_t addr) {
|
||||
#ifdef TRACE
|
||||
std::cout << sw.getElapsed() << ": prepareBlock-enter " << addr << std::endl;
|
||||
std::cout << sw.getElapsed() << ": prepareBlock-enter " << addr / CacheLineSize << std::endl;
|
||||
#endif
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
@@ -47,18 +47,24 @@ namespace RandomX {
|
||||
#ifdef TRACE
|
||||
std::cout << sw.getElapsed() << ": prepareBlock-notify " << startBlock << "/" << blockCount << std::endl;
|
||||
#endif
|
||||
notifier.notify_all();
|
||||
notifier.notify_one();
|
||||
}
|
||||
|
||||
template<bool softAes>
|
||||
const uint64_t* LightClientAsyncWorker<softAes>::getBlock(addr_t addr) {
|
||||
#ifdef TRACE
|
||||
std::cout << sw.getElapsed() << ": getBlock-enter " << addr / CacheLineSize << std::endl;
|
||||
#endif
|
||||
uint32_t currentBlock = addr / CacheLineSize;
|
||||
if (currentBlock != startBlock || output != currentLine.data()) {
|
||||
initBlock<softAes>(cache->getCache(), (uint8_t*)currentLine.data(), currentBlock, cache->getKeys());
|
||||
initBlock(cache->getCache(), (uint8_t*)currentLine.data(), currentBlock, cache->getKeys());
|
||||
}
|
||||
else {
|
||||
sync();
|
||||
}
|
||||
#ifdef TRACE
|
||||
std::cout << sw.getElapsed() << ": getBlock-return " << addr / CacheLineSize << std::endl;
|
||||
#endif
|
||||
return currentLine.data();
|
||||
}
|
||||
|
||||
@@ -73,14 +79,14 @@ namespace RandomX {
|
||||
this->blockCount = blockCount;
|
||||
output = out;
|
||||
hasWork = true;
|
||||
notifier.notify_one();
|
||||
}
|
||||
notifier.notify_all();
|
||||
}
|
||||
|
||||
template<bool softAes>
|
||||
void LightClientAsyncWorker<softAes>::getBlocks(void* out, uint32_t startBlock, uint32_t blockCount) {
|
||||
for (uint32_t i = 0; i < blockCount; ++i) {
|
||||
initBlock<softAes>(cache->getCache(), (uint8_t*)out + CacheLineSize * i, startBlock + i, cache->getKeys());
|
||||
initBlock(cache->getCache(), (uint8_t*)out + CacheLineSize * i, startBlock + i, cache->getKeys());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,10 +104,17 @@ namespace RandomX {
|
||||
for (;;) {
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
notifier.wait(lk, [this] { return hasWork; });
|
||||
getBlocks(output, startBlock, blockCount);
|
||||
#ifdef TRACE
|
||||
std::cout << sw.getElapsed() << ": runWorker-getBlocks " << startBlock << "/" << blockCount << std::endl;
|
||||
#endif
|
||||
//getBlocks(output, startBlock, blockCount);
|
||||
initBlock(cache->getCache(), (uint8_t*)output, startBlock, cache->getKeys());
|
||||
hasWork = false;
|
||||
#ifdef TRACE
|
||||
std::cout << sw.getElapsed() << ": runWorker-finished " << startBlock << "/" << blockCount << std::endl;
|
||||
#endif
|
||||
lk.unlock();
|
||||
notifier.notify_all();
|
||||
notifier.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,72 +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/>.
|
||||
*/
|
||||
|
||||
// Based on:
|
||||
// *Really* minimal PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org
|
||||
// Licensed under Apache License 2.0 (NO WARRANTY, etc. see website)
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning (disable : 4146)
|
||||
#endif
|
||||
|
||||
class Pcg32 {
|
||||
public:
|
||||
typedef uint32_t result_type;
|
||||
static constexpr result_type min() { return 0U; }
|
||||
static constexpr result_type max() { return UINT32_MAX; }
|
||||
Pcg32(const void* seed) {
|
||||
auto* u64seed = (const uint64_t*)seed;
|
||||
state = *(u64seed + 0);
|
||||
inc = *(u64seed + 1) | 1ull;
|
||||
}
|
||||
Pcg32(uint64_t state, uint64_t inc) : state(state), inc(inc | 1ull) {
|
||||
}
|
||||
result_type operator()() {
|
||||
return next();
|
||||
}
|
||||
result_type getUniform(result_type min, result_type max) {
|
||||
const result_type range = max - min;
|
||||
const result_type erange = range + 1;
|
||||
result_type ret;
|
||||
|
||||
for (;;) {
|
||||
ret = next();
|
||||
if (ret / erange < UINT32_MAX / erange || UINT32_MAX % erange == range) {
|
||||
ret %= erange;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret + min;
|
||||
}
|
||||
private:
|
||||
uint64_t state;
|
||||
uint64_t inc;
|
||||
result_type next() {
|
||||
uint64_t oldstate = state;
|
||||
// Advance internal state
|
||||
state = oldstate * 6364136223846793005ULL + inc;
|
||||
// Calculate output function (XSH RR), uses old state for max ILP
|
||||
uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
|
||||
uint32_t rot = oldstate >> 59u;
|
||||
return (xorshifted >> rot) | (xorshifted << (-rot & 31));
|
||||
}
|
||||
};
|
||||
+1
-7
@@ -18,15 +18,9 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "Program.hpp"
|
||||
#include "Pcg32.hpp"
|
||||
#include "hashAes1Rx4.hpp"
|
||||
|
||||
namespace RandomX {
|
||||
void Program::initialize(Pcg32& gen) {
|
||||
for (unsigned i = 0; i < sizeof(programBuffer) / sizeof(Pcg32::result_type); ++i) {
|
||||
*(((uint32_t*)&programBuffer) + i) = gen();
|
||||
}
|
||||
}
|
||||
|
||||
void Program::print(std::ostream& os) const {
|
||||
for (int i = 0; i < RandomX::ProgramLength; ++i) {
|
||||
auto instr = programBuffer[i];
|
||||
|
||||
+7
-4
@@ -24,22 +24,25 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
#include "common.hpp"
|
||||
#include "Instruction.hpp"
|
||||
|
||||
class Pcg32;
|
||||
|
||||
namespace RandomX {
|
||||
|
||||
class Program {
|
||||
public:
|
||||
Instruction& operator()(uint64_t pc) {
|
||||
Instruction& operator()(int pc) {
|
||||
return programBuffer[pc];
|
||||
}
|
||||
void initialize(Pcg32& gen);
|
||||
friend std::ostream& operator<<(std::ostream& os, const Program& p) {
|
||||
p.print(os);
|
||||
return os;
|
||||
}
|
||||
uint64_t getEntropy(int i) {
|
||||
return entropyBuffer[i];
|
||||
}
|
||||
private:
|
||||
void print(std::ostream&) const;
|
||||
uint64_t entropyBuffer[16];
|
||||
Instruction programBuffer[ProgramLength];
|
||||
};
|
||||
|
||||
static_assert(sizeof(Program) % 64 == 0, "Invalid size of class Program");
|
||||
}
|
||||
|
||||
+54
-14
@@ -23,32 +23,72 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
#include "blake2/blake2.h"
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include "intrinPortable.h"
|
||||
|
||||
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].u64 << std::endl << std::dec;
|
||||
os << std::hex << "r" << i << " = " << rf.r[i] << std::endl << std::dec;
|
||||
for (int i = 0; i < RandomX::RegistersCount; ++i)
|
||||
os << std::hex << "f" << i << " = " << rf.f[i].hi.u64 << " (" << rf.f[i].hi.f64 << ")" << std::endl
|
||||
<< " = " << rf.f[i].lo.u64 << " (" << rf.f[i].lo.f64 << ")" << std::endl << std::dec;
|
||||
os << std::hex << "f" << i << " = " << *(uint64_t*)&rf.f[i].hi << " (" << rf.f[i].hi << ")" << std::endl
|
||||
<< " = " << *(uint64_t*)&rf.f[i].lo << " (" << rf.f[i].lo << ")" << std::endl << std::dec;
|
||||
return os;
|
||||
}
|
||||
|
||||
namespace RandomX {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
VirtualMachine::VirtualMachine() {
|
||||
mem.ds.dataset = nullptr;
|
||||
}
|
||||
|
||||
void VirtualMachine::getResult(void* scratchpad, size_t scratchpadSize, void* out) {
|
||||
constexpr size_t smallStateLength = sizeof(RegisterFile) / sizeof(uint64_t) + 8;
|
||||
alignas(16) uint64_t smallState[smallStateLength];
|
||||
memcpy(smallState, ®, sizeof(RegisterFile));
|
||||
if (scratchpadSize > 0) {
|
||||
hashAes1Rx4<false>(scratchpad, scratchpadSize, smallState + 24);
|
||||
}
|
||||
else {
|
||||
memset(smallState + 24, 0, 64);
|
||||
}
|
||||
blake2b(out, ResultSize, smallState, sizeof(smallState), nullptr, 0);
|
||||
void VirtualMachine::resetRoundingMode() {
|
||||
initFpu();
|
||||
}
|
||||
|
||||
void VirtualMachine::initialize() {
|
||||
store64(®.a[0].lo, getSmallPositiveFloatBits(program.getEntropy(0)));
|
||||
store64(®.a[0].hi, getSmallPositiveFloatBits(program.getEntropy(1)));
|
||||
store64(®.a[1].lo, getSmallPositiveFloatBits(program.getEntropy(2)));
|
||||
store64(®.a[1].hi, getSmallPositiveFloatBits(program.getEntropy(3)));
|
||||
store64(®.a[2].lo, getSmallPositiveFloatBits(program.getEntropy(4)));
|
||||
store64(®.a[2].hi, getSmallPositiveFloatBits(program.getEntropy(5)));
|
||||
store64(®.a[3].lo, getSmallPositiveFloatBits(program.getEntropy(6)));
|
||||
store64(®.a[3].hi, getSmallPositiveFloatBits(program.getEntropy(7)));
|
||||
mem.ma = program.getEntropy(8) & CacheLineAlignMask;
|
||||
mem.mx = program.getEntropy(10);
|
||||
auto addressRegisters = program.getEntropy(12);
|
||||
readReg0 = 0 + (addressRegisters & 1);
|
||||
addressRegisters >>= 1;
|
||||
readReg1 = 2 + (addressRegisters & 1);
|
||||
addressRegisters >>= 1;
|
||||
readReg2 = 4 + (addressRegisters & 1);
|
||||
addressRegisters >>= 1;
|
||||
readReg3 = 6 + (addressRegisters & 1);
|
||||
}
|
||||
|
||||
template<bool softAes>
|
||||
void VirtualMachine::getResult(void* scratchpad, size_t scratchpadSize, void* outHash) {
|
||||
if (scratchpadSize > 0) {
|
||||
hashAes1Rx4<false>(scratchpad, scratchpadSize, ®.a);
|
||||
}
|
||||
blake2b(outHash, ResultSize, ®, 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);
|
||||
|
||||
}
|
||||
+14
-6
@@ -20,28 +20,36 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include "common.hpp"
|
||||
#include "Program.hpp"
|
||||
|
||||
namespace RandomX {
|
||||
|
||||
|
||||
|
||||
class VirtualMachine {
|
||||
public:
|
||||
VirtualMachine();
|
||||
virtual ~VirtualMachine() {}
|
||||
virtual void setDataset(dataset_t ds) = 0;
|
||||
virtual void initializeScratchpad(uint8_t* scratchpad, int32_t index) = 0;
|
||||
void setScratchpad(void* ptr) {
|
||||
scratchpad = (convertible_t*)ptr;
|
||||
scratchpad = (uint8_t*)ptr;
|
||||
}
|
||||
virtual void initializeProgram(const void* seed) = 0;
|
||||
void resetRoundingMode();
|
||||
virtual void initialize();
|
||||
virtual void execute() = 0;
|
||||
void getResult(void*, size_t, void*);
|
||||
template<bool softAes>
|
||||
void getResult(void* scratchpad, size_t scratchpadSize, void* outHash);
|
||||
const RegisterFile& getRegisterFile() {
|
||||
return reg;
|
||||
}
|
||||
Program* getProgramBuffer() {
|
||||
return &program;
|
||||
}
|
||||
protected:
|
||||
DatasetReadFunc readDataset;
|
||||
alignas(16) Program program;
|
||||
alignas(16) RegisterFile reg;
|
||||
MemoryRegisters mem;
|
||||
convertible_t* scratchpad;
|
||||
uint8_t* scratchpad;
|
||||
uint32_t readReg0, readReg1, readReg2, readReg3;
|
||||
};
|
||||
}
|
||||
@@ -12,10 +12,6 @@
|
||||
mulpd xmm1, xmm5
|
||||
mulpd xmm2, xmm6
|
||||
mulpd xmm3, xmm7
|
||||
;# xorpd xmm0, xmm15
|
||||
;# xorpd xmm1, xmm15
|
||||
;# xorpd xmm2, xmm15
|
||||
;# xorpd xmm3, xmm15
|
||||
movapd xmmword ptr [rcx+0], xmm0
|
||||
movapd xmmword ptr [rcx+16], xmm1
|
||||
movapd xmmword ptr [rcx+32], xmm2
|
||||
|
||||
@@ -18,5 +18,4 @@
|
||||
movapd xmm11, xmmword ptr [rcx+120]
|
||||
movapd xmm13, xmmword ptr [minDbl]
|
||||
movapd xmm14, xmmword ptr [absMask]
|
||||
;# xorpd xmm15, xmm15
|
||||
|
||||
movapd xmm15, xmmword ptr [signMask]
|
||||
|
||||
+5
-100
@@ -27,105 +27,10 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
#define PORTABLE_BLAKE2_IMPL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define BLAKE2_INLINE __inline
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
#define BLAKE2_INLINE __inline__
|
||||
#else
|
||||
#define BLAKE2_INLINE
|
||||
#endif
|
||||
#include "endian.h"
|
||||
|
||||
/* Argon2 Team - Begin Code */
|
||||
/*
|
||||
Not an exhaustive list, but should cover the majority of modern platforms
|
||||
Additionally, the code will always be correct---this is only a performance
|
||||
tweak.
|
||||
*/
|
||||
#if (defined(__BYTE_ORDER__) && \
|
||||
(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) || \
|
||||
defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || defined(__MIPSEL__) || \
|
||||
defined(__AARCH64EL__) || defined(__amd64__) || defined(__i386__) || \
|
||||
defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) || \
|
||||
defined(_M_ARM)
|
||||
#define NATIVE_LITTLE_ENDIAN
|
||||
#endif
|
||||
/* Argon2 Team - End Code */
|
||||
|
||||
static BLAKE2_INLINE uint32_t load32(const void *src) {
|
||||
#if defined(NATIVE_LITTLE_ENDIAN)
|
||||
uint32_t w;
|
||||
memcpy(&w, src, sizeof w);
|
||||
return w;
|
||||
#else
|
||||
const uint8_t *p = (const uint8_t *)src;
|
||||
uint32_t w = *p++;
|
||||
w |= (uint32_t)(*p++) << 8;
|
||||
w |= (uint32_t)(*p++) << 16;
|
||||
w |= (uint32_t)(*p++) << 24;
|
||||
return w;
|
||||
#endif
|
||||
}
|
||||
|
||||
static BLAKE2_INLINE uint64_t load64(const void *src) {
|
||||
#if defined(NATIVE_LITTLE_ENDIAN)
|
||||
uint64_t w;
|
||||
memcpy(&w, src, sizeof w);
|
||||
return w;
|
||||
#else
|
||||
const uint8_t *p = (const uint8_t *)src;
|
||||
uint64_t w = *p++;
|
||||
w |= (uint64_t)(*p++) << 8;
|
||||
w |= (uint64_t)(*p++) << 16;
|
||||
w |= (uint64_t)(*p++) << 24;
|
||||
w |= (uint64_t)(*p++) << 32;
|
||||
w |= (uint64_t)(*p++) << 40;
|
||||
w |= (uint64_t)(*p++) << 48;
|
||||
w |= (uint64_t)(*p++) << 56;
|
||||
return w;
|
||||
#endif
|
||||
}
|
||||
|
||||
static BLAKE2_INLINE void store32(void *dst, uint32_t w) {
|
||||
#if defined(NATIVE_LITTLE_ENDIAN)
|
||||
memcpy(dst, &w, sizeof w);
|
||||
#else
|
||||
uint8_t *p = (uint8_t *)dst;
|
||||
*p++ = (uint8_t)w;
|
||||
w >>= 8;
|
||||
*p++ = (uint8_t)w;
|
||||
w >>= 8;
|
||||
*p++ = (uint8_t)w;
|
||||
w >>= 8;
|
||||
*p++ = (uint8_t)w;
|
||||
#endif
|
||||
}
|
||||
|
||||
static BLAKE2_INLINE void store64(void *dst, uint64_t w) {
|
||||
#if defined(NATIVE_LITTLE_ENDIAN)
|
||||
memcpy(dst, &w, sizeof w);
|
||||
#else
|
||||
uint8_t *p = (uint8_t *)dst;
|
||||
*p++ = (uint8_t)w;
|
||||
w >>= 8;
|
||||
*p++ = (uint8_t)w;
|
||||
w >>= 8;
|
||||
*p++ = (uint8_t)w;
|
||||
w >>= 8;
|
||||
*p++ = (uint8_t)w;
|
||||
w >>= 8;
|
||||
*p++ = (uint8_t)w;
|
||||
w >>= 8;
|
||||
*p++ = (uint8_t)w;
|
||||
w >>= 8;
|
||||
*p++ = (uint8_t)w;
|
||||
w >>= 8;
|
||||
*p++ = (uint8_t)w;
|
||||
#endif
|
||||
}
|
||||
|
||||
static BLAKE2_INLINE uint64_t load48(const void *src) {
|
||||
static FORCE_INLINE uint64_t load48(const void *src) {
|
||||
const uint8_t *p = (const uint8_t *)src;
|
||||
uint64_t w = *p++;
|
||||
w |= (uint64_t)(*p++) << 8;
|
||||
@@ -136,7 +41,7 @@ static BLAKE2_INLINE uint64_t load48(const void *src) {
|
||||
return w;
|
||||
}
|
||||
|
||||
static BLAKE2_INLINE void store48(void *dst, uint64_t w) {
|
||||
static FORCE_INLINE void store48(void *dst, uint64_t w) {
|
||||
uint8_t *p = (uint8_t *)dst;
|
||||
*p++ = (uint8_t)w;
|
||||
w >>= 8;
|
||||
@@ -151,11 +56,11 @@ static BLAKE2_INLINE void store48(void *dst, uint64_t w) {
|
||||
*p++ = (uint8_t)w;
|
||||
}
|
||||
|
||||
static BLAKE2_INLINE uint32_t rotr32(const uint32_t w, const unsigned c) {
|
||||
static FORCE_INLINE uint32_t rotr32(const uint32_t w, const unsigned c) {
|
||||
return (w >> c) | (w << (32 - c));
|
||||
}
|
||||
|
||||
static BLAKE2_INLINE uint64_t rotr64(const uint64_t w, const unsigned c) {
|
||||
static FORCE_INLINE uint64_t rotr64(const uint64_t w, const unsigned c) {
|
||||
return (w >> c) | (w << (64 - c));
|
||||
}
|
||||
|
||||
|
||||
@@ -51,29 +51,29 @@ static const unsigned int blake2b_sigma[12][16] = {
|
||||
{14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
|
||||
};
|
||||
|
||||
static BLAKE2_INLINE void blake2b_set_lastnode(blake2b_state *S) {
|
||||
static FORCE_INLINE void blake2b_set_lastnode(blake2b_state *S) {
|
||||
S->f[1] = (uint64_t)-1;
|
||||
}
|
||||
|
||||
static BLAKE2_INLINE void blake2b_set_lastblock(blake2b_state *S) {
|
||||
static FORCE_INLINE void blake2b_set_lastblock(blake2b_state *S) {
|
||||
if (S->last_node) {
|
||||
blake2b_set_lastnode(S);
|
||||
}
|
||||
S->f[0] = (uint64_t)-1;
|
||||
}
|
||||
|
||||
static BLAKE2_INLINE void blake2b_increment_counter(blake2b_state *S,
|
||||
static FORCE_INLINE void blake2b_increment_counter(blake2b_state *S,
|
||||
uint64_t inc) {
|
||||
S->t[0] += inc;
|
||||
S->t[1] += (S->t[0] < inc);
|
||||
}
|
||||
|
||||
static BLAKE2_INLINE void blake2b_invalidate_state(blake2b_state *S) {
|
||||
static FORCE_INLINE void blake2b_invalidate_state(blake2b_state *S) {
|
||||
//clear_internal_memory(S, sizeof(*S)); /* wipe */
|
||||
blake2b_set_lastblock(S); /* invalidate for further use */
|
||||
}
|
||||
|
||||
static BLAKE2_INLINE void blake2b_init0(blake2b_state *S) {
|
||||
static FORCE_INLINE void blake2b_init0(blake2b_state *S) {
|
||||
memset(S, 0, sizeof(*S));
|
||||
memcpy(S->h, blake2b_IV, sizeof(S->h));
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
#include "blake2-impl.h"
|
||||
|
||||
/* designed by the Lyra PHC team */
|
||||
static BLAKE2_INLINE uint64_t fBlaMka(uint64_t x, uint64_t y) {
|
||||
static FORCE_INLINE uint64_t fBlaMka(uint64_t x, uint64_t y) {
|
||||
const uint64_t m = UINT64_C(0xFFFFFFFF);
|
||||
const uint64_t xy = (x & m) * (y & m);
|
||||
return x + y + 2 * xy;
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define FORCE_INLINE __inline
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
#define FORCE_INLINE __inline__
|
||||
#else
|
||||
#define FORCE_INLINE
|
||||
#endif
|
||||
|
||||
/* Argon2 Team - Begin Code */
|
||||
/*
|
||||
Not an exhaustive list, but should cover the majority of modern platforms
|
||||
Additionally, the code will always be correct---this is only a performance
|
||||
tweak.
|
||||
*/
|
||||
#if (defined(__BYTE_ORDER__) && \
|
||||
(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) || \
|
||||
defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || defined(__MIPSEL__) || \
|
||||
defined(__AARCH64EL__) || defined(__amd64__) || defined(__i386__) || \
|
||||
defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) || \
|
||||
defined(_M_ARM)
|
||||
#define NATIVE_LITTLE_ENDIAN
|
||||
#endif
|
||||
/* Argon2 Team - End Code */
|
||||
|
||||
static FORCE_INLINE uint32_t load32(const void *src) {
|
||||
#if defined(NATIVE_LITTLE_ENDIAN)
|
||||
uint32_t w;
|
||||
memcpy(&w, src, sizeof w);
|
||||
return w;
|
||||
#else
|
||||
const uint8_t *p = (const uint8_t *)src;
|
||||
uint32_t w = *p++;
|
||||
w |= (uint32_t)(*p++) << 8;
|
||||
w |= (uint32_t)(*p++) << 16;
|
||||
w |= (uint32_t)(*p++) << 24;
|
||||
return w;
|
||||
#endif
|
||||
}
|
||||
|
||||
static FORCE_INLINE uint64_t load64(const void *src) {
|
||||
#if defined(NATIVE_LITTLE_ENDIAN)
|
||||
uint64_t w;
|
||||
memcpy(&w, src, sizeof w);
|
||||
return w;
|
||||
#else
|
||||
const uint8_t *p = (const uint8_t *)src;
|
||||
uint64_t w = *p++;
|
||||
w |= (uint64_t)(*p++) << 8;
|
||||
w |= (uint64_t)(*p++) << 16;
|
||||
w |= (uint64_t)(*p++) << 24;
|
||||
w |= (uint64_t)(*p++) << 32;
|
||||
w |= (uint64_t)(*p++) << 40;
|
||||
w |= (uint64_t)(*p++) << 48;
|
||||
w |= (uint64_t)(*p++) << 56;
|
||||
return w;
|
||||
#endif
|
||||
}
|
||||
|
||||
static FORCE_INLINE void store32(void *dst, uint32_t w) {
|
||||
#if defined(NATIVE_LITTLE_ENDIAN)
|
||||
memcpy(dst, &w, sizeof w);
|
||||
#else
|
||||
uint8_t *p = (uint8_t *)dst;
|
||||
*p++ = (uint8_t)w;
|
||||
w >>= 8;
|
||||
*p++ = (uint8_t)w;
|
||||
w >>= 8;
|
||||
*p++ = (uint8_t)w;
|
||||
w >>= 8;
|
||||
*p++ = (uint8_t)w;
|
||||
#endif
|
||||
}
|
||||
|
||||
static FORCE_INLINE void store64(void *dst, uint64_t w) {
|
||||
#if defined(NATIVE_LITTLE_ENDIAN)
|
||||
memcpy(dst, &w, sizeof w);
|
||||
#else
|
||||
uint8_t *p = (uint8_t *)dst;
|
||||
*p++ = (uint8_t)w;
|
||||
w >>= 8;
|
||||
*p++ = (uint8_t)w;
|
||||
w >>= 8;
|
||||
*p++ = (uint8_t)w;
|
||||
w >>= 8;
|
||||
*p++ = (uint8_t)w;
|
||||
w >>= 8;
|
||||
*p++ = (uint8_t)w;
|
||||
w >>= 8;
|
||||
*p++ = (uint8_t)w;
|
||||
w >>= 8;
|
||||
*p++ = (uint8_t)w;
|
||||
w >>= 8;
|
||||
*p++ = (uint8_t)w;
|
||||
#endif
|
||||
}
|
||||
+27
-26
@@ -21,13 +21,14 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include "blake2/endian.h"
|
||||
|
||||
namespace RandomX {
|
||||
|
||||
using addr_t = uint32_t;
|
||||
|
||||
constexpr int SeedSize = 32;
|
||||
constexpr int ResultSize = 32;
|
||||
constexpr int ResultSize = 64;
|
||||
|
||||
constexpr int ArgonIterations = 3;
|
||||
constexpr uint32_t ArgonMemorySize = 262144; //KiB
|
||||
@@ -36,12 +37,13 @@ namespace RandomX {
|
||||
constexpr int ArgonSaltSize = sizeof(ArgonSalt) - 1;
|
||||
|
||||
constexpr int CacheLineSize = 64;
|
||||
constexpr uint32_t CacheLineAlignMask = 0xFFFFFFFF & ~(CacheLineSize - 1);
|
||||
constexpr uint64_t DatasetSize = 4ULL * 1024 * 1024 * 1024; //4 GiB
|
||||
constexpr uint32_t CacheSize = ArgonMemorySize * 1024;
|
||||
constexpr int CacheBlockCount = CacheSize / CacheLineSize;
|
||||
constexpr int BlockExpansionRatio = DatasetSize / CacheSize;
|
||||
constexpr int DatasetBlockCount = BlockExpansionRatio * CacheBlockCount;
|
||||
constexpr int DatasetIterations = 10;
|
||||
constexpr int DatasetIterations = 16;
|
||||
|
||||
|
||||
#ifdef TRACE
|
||||
@@ -50,35 +52,36 @@ namespace RandomX {
|
||||
constexpr bool trace = false;
|
||||
#endif
|
||||
|
||||
union convertible_t {
|
||||
double f64;
|
||||
int64_t i64;
|
||||
uint64_t u64;
|
||||
int32_t i32;
|
||||
uint32_t u32;
|
||||
struct {
|
||||
int32_t i32lo;
|
||||
int32_t i32hi;
|
||||
};
|
||||
};
|
||||
#ifndef UNREACHABLE
|
||||
#ifdef __GNUC__
|
||||
#define UNREACHABLE __builtin_unreachable()
|
||||
#elif _MSC_VER
|
||||
#define UNREACHABLE __assume(false)
|
||||
#else
|
||||
#define UNREACHABLE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
using int_reg_t = uint64_t;
|
||||
|
||||
struct fpu_reg_t {
|
||||
convertible_t lo;
|
||||
convertible_t hi;
|
||||
double lo;
|
||||
double hi;
|
||||
};
|
||||
|
||||
constexpr int ProgramLength = 256;
|
||||
constexpr uint32_t InstructionCount = 2048;
|
||||
constexpr uint32_t ScratchpadSize = 2 * 1024 * 1024;
|
||||
constexpr uint32_t ScratchpadLength = ScratchpadSize / sizeof(convertible_t);
|
||||
constexpr uint32_t ScratchpadL1 = ScratchpadSize / 128 / sizeof(convertible_t);
|
||||
constexpr uint32_t ScratchpadL2 = ScratchpadSize / 8 / sizeof(convertible_t);
|
||||
constexpr uint32_t ScratchpadL3 = ScratchpadSize / sizeof(convertible_t);
|
||||
constexpr uint32_t ScratchpadLength = ScratchpadSize / sizeof(int_reg_t);
|
||||
constexpr uint32_t ScratchpadL1 = ScratchpadSize / 128 / sizeof(int_reg_t);
|
||||
constexpr uint32_t ScratchpadL2 = ScratchpadSize / 8 / sizeof(int_reg_t);
|
||||
constexpr uint32_t ScratchpadL3 = ScratchpadSize / sizeof(int_reg_t);
|
||||
constexpr int ScratchpadL1Mask = (ScratchpadL1 - 1) * 8;
|
||||
constexpr int ScratchpadL2Mask = (ScratchpadL2 - 1) * 8;
|
||||
constexpr int ScratchpadL1Mask16 = (ScratchpadL1 / 2 - 1) * 16;
|
||||
constexpr int ScratchpadL2Mask16 = (ScratchpadL2 / 2 - 1) * 16;
|
||||
constexpr int ScratchpadL3Mask = (ScratchpadLength - 1) * 8;
|
||||
constexpr int ScratchpadL3Mask64 = (ScratchpadLength / 8 - 1) * 64;
|
||||
constexpr uint32_t TransformationCount = 90;
|
||||
constexpr int RegistersCount = 8;
|
||||
|
||||
@@ -117,22 +120,20 @@ namespace RandomX {
|
||||
static_assert(sizeof(MemoryRegisters) == 2 * sizeof(addr_t) + sizeof(uintptr_t), "Invalid alignment of struct RandomX::MemoryRegisters");
|
||||
|
||||
struct RegisterFile {
|
||||
convertible_t r[RegistersCount];
|
||||
int_reg_t r[RegistersCount];
|
||||
fpu_reg_t f[RegistersCount / 2];
|
||||
fpu_reg_t g[RegistersCount / 2];
|
||||
fpu_reg_t e[RegistersCount / 2];
|
||||
fpu_reg_t a[RegistersCount / 2];
|
||||
};
|
||||
|
||||
static_assert(sizeof(RegisterFile) == 256, "Invalid alignment of struct RandomX::RegisterFile");
|
||||
|
||||
typedef void(*DatasetReadFunc)(addr_t, MemoryRegisters&, RegisterFile&);
|
||||
typedef void(*DatasetReadFunc)(addr_t, MemoryRegisters&, int_reg_t(®)[RegistersCount]);
|
||||
|
||||
typedef void(*ProgramFunc)(RegisterFile&, MemoryRegisters&, convertible_t*, uint64_t);
|
||||
|
||||
typedef bool(*Condition)(convertible_t&, convertible_t&);
|
||||
typedef void(*ProgramFunc)(RegisterFile&, MemoryRegisters&, uint8_t* /* scratchpad */, uint64_t);
|
||||
|
||||
extern "C" {
|
||||
void executeProgram(RegisterFile&, MemoryRegisters&, convertible_t*, uint64_t);
|
||||
void executeProgram(RegisterFile&, MemoryRegisters&, uint8_t* /* scratchpad */, uint64_t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+32
-57
@@ -24,11 +24,11 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "common.hpp"
|
||||
#include "dataset.hpp"
|
||||
#include "Pcg32.hpp"
|
||||
#include "Cache.hpp"
|
||||
#include "virtualMemory.hpp"
|
||||
#include "softAes.h"
|
||||
#include "squareHash.h"
|
||||
#include "blake2/endian.h"
|
||||
|
||||
#if defined(__SSE2__)
|
||||
#include <wmmintrin.h>
|
||||
@@ -39,56 +39,38 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace RandomX {
|
||||
|
||||
template<typename T>
|
||||
static inline void shuffle(T* buffer, size_t bytes, Pcg32& gen) {
|
||||
auto count = bytes / sizeof(T);
|
||||
for (auto i = count - 1; i >= 1; --i) {
|
||||
int j = gen.getUniform(0, i);
|
||||
std::swap(buffer[j], buffer[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template<bool soft>
|
||||
void initBlock(const uint8_t* intermediate, uint8_t* out, uint32_t blockNumber, const KeysContainer& keys) {
|
||||
void initBlock(const uint8_t* cache, uint8_t* out, uint32_t blockNumber, const KeysContainer& keys) {
|
||||
uint64_t r0, r1, r2, r3, r4, r5, r6, r7;
|
||||
|
||||
r0 = 4ULL * blockNumber;
|
||||
r1 = r2 = r3 = r4 = r5 = r6 = r7 = 0;
|
||||
|
||||
constexpr int mask = (CacheSize - 1) & -64;
|
||||
constexpr uint32_t mask = (CacheSize - 1) & CacheLineAlignMask;
|
||||
|
||||
for (auto i = 0; i < DatasetIterations; ++i) {
|
||||
uint64_t* mix = (uint64_t*)(intermediate + (r0 & mask));
|
||||
PREFETCHNTA(mix);
|
||||
const uint8_t* mixBlock = cache + (r0 & mask);
|
||||
PREFETCHNTA(mixBlock);
|
||||
r0 = squareHash(r0);
|
||||
r0 ^= mix[0];
|
||||
r1 ^= mix[1];
|
||||
r2 ^= mix[2];
|
||||
r3 ^= mix[3];
|
||||
r4 ^= mix[4];
|
||||
r5 ^= mix[5];
|
||||
r6 ^= mix[6];
|
||||
r7 ^= mix[7];
|
||||
r0 ^= load64(mixBlock + 0);
|
||||
r1 ^= load64(mixBlock + 8);
|
||||
r2 ^= load64(mixBlock + 16);
|
||||
r3 ^= load64(mixBlock + 24);
|
||||
r4 ^= load64(mixBlock + 32);
|
||||
r5 ^= load64(mixBlock + 40);
|
||||
r6 ^= load64(mixBlock + 48);
|
||||
r7 ^= load64(mixBlock + 56);
|
||||
}
|
||||
|
||||
uint64_t* out64 = (uint64_t*)out;
|
||||
|
||||
out64[0] = r0;
|
||||
out64[1] = r1;
|
||||
out64[2] = r2;
|
||||
out64[3] = r3;
|
||||
out64[4] = r4;
|
||||
out64[5] = r5;
|
||||
out64[6] = r6;
|
||||
out64[7] = r7;
|
||||
store64(out + 0, r0);
|
||||
store64(out + 8, r1);
|
||||
store64(out + 16, r2);
|
||||
store64(out + 24, r3);
|
||||
store64(out + 32, r4);
|
||||
store64(out + 40, r5);
|
||||
store64(out + 48, r6);
|
||||
store64(out + 56, r7);
|
||||
}
|
||||
|
||||
template
|
||||
void initBlock<true>(const uint8_t*, uint8_t*, uint32_t, const KeysContainer&);
|
||||
|
||||
template
|
||||
void initBlock<false>(const uint8_t*, uint8_t*, uint32_t, const KeysContainer&);
|
||||
|
||||
void datasetRead(addr_t addr, MemoryRegisters& memory, RegisterFile& reg) {
|
||||
uint64_t* datasetLine = (uint64_t*)(memory.ds.dataset + memory.ma);
|
||||
memory.mx ^= addr;
|
||||
@@ -96,34 +78,27 @@ namespace RandomX {
|
||||
std::swap(memory.mx, memory.ma);
|
||||
PREFETCHNTA(memory.ds.dataset + memory.ma);
|
||||
for (int i = 0; i < RegistersCount; ++i)
|
||||
reg.r[i].u64 ^= datasetLine[i];
|
||||
reg.r[i] ^= datasetLine[i];
|
||||
}
|
||||
|
||||
template<bool softAes>
|
||||
void datasetReadLight(addr_t addr, MemoryRegisters& memory, RegisterFile& reg) {
|
||||
void datasetReadLight(addr_t addr, MemoryRegisters& memory, int_reg_t (®)[RegistersCount]) {
|
||||
memory.mx ^= addr;
|
||||
memory.mx &= CacheLineAlignMask; //align to cache line
|
||||
Cache* cache = memory.ds.cache;
|
||||
uint64_t datasetLine[CacheLineSize / sizeof(uint64_t)];
|
||||
initBlock<softAes>(cache->getCache(), (uint8_t*)datasetLine, memory.ma / CacheLineSize, cache->getKeys());
|
||||
initBlock(cache->getCache(), (uint8_t*)datasetLine, memory.ma / CacheLineSize, cache->getKeys());
|
||||
for (int i = 0; i < RegistersCount; ++i)
|
||||
reg.r[i].u64 ^= datasetLine[i];
|
||||
memory.mx ^= addr;
|
||||
memory.mx &= -64; //align to cache line
|
||||
reg[i] ^= datasetLine[i];
|
||||
std::swap(memory.mx, memory.ma);
|
||||
}
|
||||
|
||||
template
|
||||
void datasetReadLight<false>(addr_t addr, MemoryRegisters& memory, RegisterFile& reg);
|
||||
|
||||
template
|
||||
void datasetReadLight<true>(addr_t addr, MemoryRegisters& memory, RegisterFile& reg);
|
||||
|
||||
void datasetReadLightAsync(addr_t addr, MemoryRegisters& memory, RegisterFile& reg) {
|
||||
void datasetReadLightAsync(addr_t addr, MemoryRegisters& memory, int_reg_t(®)[RegistersCount]) {
|
||||
ILightClientAsyncWorker* aw = memory.ds.asyncWorker;
|
||||
const uint64_t* datasetLine = aw->getBlock(memory.ma);
|
||||
for (int i = 0; i < RegistersCount; ++i)
|
||||
reg.r[i].u64 ^= datasetLine[i];
|
||||
reg[i] ^= datasetLine[i];
|
||||
memory.mx ^= addr;
|
||||
memory.mx &= -64; //align to cache line
|
||||
memory.mx &= CacheLineAlignMask; //align to cache line
|
||||
std::swap(memory.mx, memory.ma);
|
||||
aw->prepareBlock(memory.ma);
|
||||
}
|
||||
@@ -145,7 +120,7 @@ namespace RandomX {
|
||||
template<bool softAes>
|
||||
void datasetInit(Cache* cache, dataset_t ds, uint32_t startBlock, uint32_t blockCount) {
|
||||
for (uint32_t i = startBlock; i < startBlock + blockCount; ++i) {
|
||||
initBlock<softAes>(cache->getCache(), ds.dataset + i * CacheLineSize, i, cache->getKeys());
|
||||
initBlock(cache->getCache(), ds.dataset + i * CacheLineSize, i, cache->getKeys());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +147,7 @@ namespace RandomX {
|
||||
alignas(16) KeysContainer keys;
|
||||
alignas(16) uint8_t buffer[CacheLineSize];
|
||||
for (uint32_t block = 0; block < blockCount; ++block) {
|
||||
initBlock<softAes>(buffer, buffer, 0, keys);
|
||||
initBlock(buffer, buffer, 0, keys);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-4
@@ -31,7 +31,6 @@ namespace RandomX {
|
||||
template<bool soft, bool enc>
|
||||
void initBlock(const uint8_t* in, uint8_t* out, uint32_t blockNumber, const KeysContainer& keys);
|
||||
|
||||
template<bool softAes>
|
||||
void initBlock(const uint8_t* cache, uint8_t* block, uint32_t blockNumber, const KeysContainer& keys);
|
||||
|
||||
void datasetAlloc(dataset_t& ds, bool largePages);
|
||||
@@ -44,10 +43,9 @@ namespace RandomX {
|
||||
template<bool softAes>
|
||||
void datasetInitCache(const void* seed, dataset_t& dataset, bool largePages);
|
||||
|
||||
template<bool softAes>
|
||||
void datasetReadLight(addr_t addr, MemoryRegisters& memory, RegisterFile&);
|
||||
void datasetReadLight(addr_t addr, MemoryRegisters& memory, int_reg_t(®)[RegistersCount]);
|
||||
|
||||
void datasetReadLightAsync(addr_t addr, MemoryRegisters& memory, RegisterFile& reg);
|
||||
void datasetReadLightAsync(addr_t addr, MemoryRegisters& memory, int_reg_t(®)[RegistersCount]);
|
||||
|
||||
template<bool softAes>
|
||||
void aesBench(uint32_t blockCount);
|
||||
|
||||
@@ -54,7 +54,7 @@ executeProgram PROC
|
||||
; xmm12 -> temporary
|
||||
; xmm13 -> DBL_MIN
|
||||
; xmm14 -> absolute value mask
|
||||
; xmm15 -> unused
|
||||
; xmm15 -> sign mask
|
||||
|
||||
; store callee-saved registers
|
||||
push rbx
|
||||
@@ -104,7 +104,7 @@ executeProgram PROC
|
||||
movapd xmm11, xmmword ptr [rcx+120]
|
||||
movapd xmm13, xmmword ptr [minDbl]
|
||||
movapd xmm14, xmmword ptr [absMask]
|
||||
;# xorps xmm15, xmm15
|
||||
movapd xmm15, xmmword ptr [signMask]
|
||||
|
||||
jmp program_begin
|
||||
|
||||
|
||||
@@ -37,8 +37,8 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
#define WT_INEG_R 2
|
||||
#define WT_IXOR_R 16
|
||||
#define WT_IXOR_M 4
|
||||
#define WT_IROR_R 8
|
||||
#define WT_IROL_R 8
|
||||
#define WT_IROR_R 10
|
||||
#define WT_IROL_R 0
|
||||
#define WT_ISWAP_R 4
|
||||
|
||||
//Common floating point
|
||||
@@ -49,6 +49,7 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
#define WT_FADD_M 5
|
||||
#define WT_FSUB_R 20
|
||||
#define WT_FSUB_M 5
|
||||
#define WT_FNEG_R 6
|
||||
|
||||
//Floating point group E
|
||||
#define WT_FMUL_R 20
|
||||
@@ -61,7 +62,6 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
#define WT_COND_R 7
|
||||
#define WT_COND_M 1
|
||||
#define WT_CFROUND 1
|
||||
#define WT_CFSUM_R 0
|
||||
|
||||
//Store
|
||||
#define WT_ISTORE 16
|
||||
@@ -74,7 +74,7 @@ WT_ISUB_M + WT_IMUL_9C + WT_IMUL_R + WT_IMUL_M + WT_IMULH_R + \
|
||||
WT_IMULH_M + WT_ISMULH_R + WT_ISMULH_M + WT_IDIV_C + WT_ISDIV_C + \
|
||||
WT_INEG_R + WT_IXOR_R + WT_IXOR_M + WT_IROR_R + WT_IROL_R + \
|
||||
WT_ISWAP_R + WT_FSWAP_R + WT_FADD_R + WT_FADD_M + WT_FSUB_R + WT_FSUB_M + \
|
||||
WT_CFSUM_R + WT_FMUL_R + WT_FMUL_M + WT_FDIV_R + WT_FDIV_M + \
|
||||
WT_FNEG_R + WT_FMUL_R + WT_FMUL_M + WT_FDIV_R + WT_FDIV_M + \
|
||||
WT_FSQRT_R + WT_COND_R + WT_COND_M + WT_CFROUND + WT_ISTORE + WT_FSTORE + WT_NOP;
|
||||
|
||||
static_assert(wtSum == 256,
|
||||
|
||||
@@ -1,57 +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 <cstdint>
|
||||
#include "common.hpp"
|
||||
|
||||
namespace RandomX {
|
||||
|
||||
extern "C" {
|
||||
void ADD_64(convertible_t& a, convertible_t& b, convertible_t& c);
|
||||
void ADD_32(convertible_t& a, convertible_t& b, convertible_t& c);
|
||||
void SUB_64(convertible_t& a, convertible_t& b, convertible_t& c);
|
||||
void SUB_32(convertible_t& a, convertible_t& b, convertible_t& c);
|
||||
void MUL_64(convertible_t& a, convertible_t& b, convertible_t& c);
|
||||
void MULH_64(convertible_t& a, convertible_t& b, convertible_t& c);
|
||||
void MUL_32(convertible_t& a, convertible_t& b, convertible_t& c);
|
||||
void IMUL_32(convertible_t& a, convertible_t& b, convertible_t& c);
|
||||
void IMULH_64(convertible_t& a, convertible_t& b, convertible_t& c);
|
||||
void DIV_64(convertible_t& a, convertible_t& b, convertible_t& c);
|
||||
void IDIV_64(convertible_t& a, convertible_t& b, convertible_t& c);
|
||||
void AND_64(convertible_t& a, convertible_t& b, convertible_t& c);
|
||||
void AND_32(convertible_t& a, convertible_t& b, convertible_t& c);
|
||||
void OR_64(convertible_t& a, convertible_t& b, convertible_t& c);
|
||||
void OR_32(convertible_t& a, convertible_t& b, convertible_t& c);
|
||||
void XOR_64(convertible_t& a, convertible_t& b, convertible_t& c);
|
||||
void XOR_32(convertible_t& a, convertible_t& b, convertible_t& c);
|
||||
void SHL_64(convertible_t& a, convertible_t& b, convertible_t& c);
|
||||
void SHR_64(convertible_t& a, convertible_t& b, convertible_t& c);
|
||||
void SAR_64(convertible_t& a, convertible_t& b, convertible_t& c);
|
||||
void ROL_64(convertible_t& a, convertible_t& b, convertible_t& c);
|
||||
void ROR_64(convertible_t& a, convertible_t& b, convertible_t& c);
|
||||
bool JMP_COND(uint8_t, convertible_t&, int32_t);
|
||||
void FPINIT();
|
||||
void FPROUND(convertible_t, uint8_t);
|
||||
void FADD(convertible_t& a, fpu_reg_t& b, fpu_reg_t& c);
|
||||
void FSUB(convertible_t& a, fpu_reg_t& b, fpu_reg_t& c);
|
||||
void FMUL(convertible_t& a, fpu_reg_t& b, fpu_reg_t& c);
|
||||
void FDIV(convertible_t& a, fpu_reg_t& b, fpu_reg_t& c);
|
||||
void FSQRT(convertible_t& a, fpu_reg_t& b, fpu_reg_t& c);
|
||||
}
|
||||
}
|
||||
+49
-130
@@ -18,12 +18,14 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
//#define DEBUG
|
||||
#include "intrinPortable.h"
|
||||
#include "blake2/endian.h"
|
||||
#pragma STDC FENV_ACCESS on
|
||||
#include <cfenv>
|
||||
#include <cmath>
|
||||
#ifdef DEBUG
|
||||
#include <iostream>
|
||||
#endif
|
||||
#include "common.hpp"
|
||||
|
||||
#if defined(__SIZEOF_INT128__)
|
||||
typedef unsigned __int128 uint128_t;
|
||||
@@ -136,18 +138,18 @@ static inline int32_t safeSub(int32_t a, int32_t b) {
|
||||
|
||||
#if defined(__has_builtin)
|
||||
#if __has_builtin(__builtin_sub_overflow)
|
||||
static inline bool subOverflow__(int32_t a, int32_t b) {
|
||||
static inline bool subOverflow__(uint32_t a, uint32_t b) {
|
||||
int32_t temp;
|
||||
return __builtin_sub_overflow(a, b, &temp);
|
||||
return __builtin_sub_overflow(unsigned32ToSigned2sCompl(a), unsigned32ToSigned2sCompl(b), &temp);
|
||||
}
|
||||
#define HAVE_SUB_OVERFLOW
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_SUB_OVERFLOW
|
||||
static inline bool subOverflow__(int32_t a, int32_t b) {
|
||||
auto c = safeSub(a, b);
|
||||
return (c < a) != (b > 0);
|
||||
static inline bool subOverflow__(uint32_t a, uint32_t b) {
|
||||
auto c = unsigned32ToSigned2sCompl(a - b);
|
||||
return (c < unsigned32ToSigned2sCompl(a)) != (unsigned32ToSigned2sCompl(b) > 0);
|
||||
}
|
||||
#define HAVE_SUB_OVERFLOW
|
||||
#endif
|
||||
@@ -166,40 +168,44 @@ static inline double FlushNaN(double x) {
|
||||
|
||||
void setRoundMode(uint32_t rcflag) {
|
||||
switch (rcflag & 3) {
|
||||
case RoundDown:
|
||||
setRoundMode__(FE_DOWNWARD);
|
||||
break;
|
||||
case RoundUp:
|
||||
setRoundMode__(FE_UPWARD);
|
||||
break;
|
||||
case RoundToZero:
|
||||
setRoundMode__(FE_TOWARDZERO);
|
||||
break;
|
||||
default:
|
||||
setRoundMode__(FE_TONEAREST);
|
||||
break;
|
||||
case RoundDown:
|
||||
setRoundMode__(FE_DOWNWARD);
|
||||
break;
|
||||
case RoundUp:
|
||||
setRoundMode__(FE_UPWARD);
|
||||
break;
|
||||
case RoundToZero:
|
||||
setRoundMode__(FE_TOWARDZERO);
|
||||
break;
|
||||
case RoundToNearest:
|
||||
setRoundMode__(FE_TONEAREST);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE;
|
||||
}
|
||||
}
|
||||
|
||||
bool condition(uint32_t type, int32_t value, int32_t imm32) {
|
||||
bool condition(uint32_t type, uint32_t value, uint32_t imm32) {
|
||||
switch (type & 7)
|
||||
{
|
||||
case 0:
|
||||
return (uint32_t)value <= (uint32_t)imm32;
|
||||
case 1:
|
||||
return (uint32_t)value > (uint32_t)imm32;
|
||||
case 2:
|
||||
return safeSub(value, imm32) < 0;
|
||||
case 3:
|
||||
return safeSub(value, imm32) >= 0;
|
||||
case 4:
|
||||
return subOverflow__(value, imm32);
|
||||
case 5:
|
||||
return !subOverflow__(value, imm32);
|
||||
case 6:
|
||||
return value < imm32;
|
||||
case 7:
|
||||
return value >= imm32;
|
||||
case 0:
|
||||
return value <= imm32;
|
||||
case 1:
|
||||
return value > imm32;
|
||||
case 2:
|
||||
return unsigned32ToSigned2sCompl(value - imm32) < 0;
|
||||
case 3:
|
||||
return unsigned32ToSigned2sCompl(value - imm32) >= 0;
|
||||
case 4:
|
||||
return subOverflow__(value, imm32);
|
||||
case 5:
|
||||
return !subOverflow__(value, imm32);
|
||||
case 6:
|
||||
return unsigned32ToSigned2sCompl(value) < unsigned32ToSigned2sCompl(imm32);
|
||||
case 7:
|
||||
return unsigned32ToSigned2sCompl(value) >= unsigned32ToSigned2sCompl(imm32);
|
||||
default:
|
||||
UNREACHABLE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,100 +217,13 @@ void initFpu() {
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace RandomX {
|
||||
union double_ser_t {
|
||||
double f;
|
||||
uint64_t i;
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
/*void DIV_64(convertible_t& a, convertible_t& b, convertible_t& c) {
|
||||
c.u64 = a.u64 / (b.u32 != 0 ? b.u32 : 1U);
|
||||
}
|
||||
|
||||
void IDIV_64(convertible_t& a, convertible_t& b, convertible_t& c) {
|
||||
if (a.i64 == INT64_MIN && b.i32 == -1)
|
||||
c.i64 = INT64_MIN;
|
||||
else
|
||||
c.i64 = a.i64 / (b.i32 != 0 ? b.i32 : 1);
|
||||
}
|
||||
|
||||
void FADD(convertible_t& a, fpu_reg_t& b, fpu_reg_t& c) {
|
||||
#ifdef __SSE2__
|
||||
__m128i ai = _mm_loadl_epi64((const __m128i*)&a);
|
||||
__m128d ad = _mm_cvtepi32_pd(ai);
|
||||
__m128d bd = _mm_load_pd(&b.lo.f64);
|
||||
__m128d cd = _mm_add_pd(ad, bd);
|
||||
_mm_store_pd(&c.lo.f64, cd);
|
||||
#else
|
||||
double alo = (double)a.i32lo;
|
||||
double ahi = (double)a.i32hi;
|
||||
c.lo.f64 = alo + b.lo.f64;
|
||||
c.hi.f64 = ahi + b.hi.f64;
|
||||
#endif
|
||||
}
|
||||
|
||||
void FSUB(convertible_t& a, fpu_reg_t& b, fpu_reg_t& c) {
|
||||
#ifdef __SSE2__
|
||||
__m128i ai = _mm_loadl_epi64((const __m128i*)&a);
|
||||
__m128d ad = _mm_cvtepi32_pd(ai);
|
||||
__m128d bd = _mm_load_pd(&b.lo.f64);
|
||||
__m128d cd = _mm_sub_pd(ad, bd);
|
||||
_mm_store_pd(&c.lo.f64, cd);
|
||||
#else
|
||||
double alo = (double)a.i32lo;
|
||||
double ahi = (double)a.i32hi;
|
||||
c.lo.f64 = alo - b.lo.f64;
|
||||
c.hi.f64 = ahi - b.hi.f64;
|
||||
#endif
|
||||
}
|
||||
|
||||
void FMUL(convertible_t& a, fpu_reg_t& b, fpu_reg_t& c) {
|
||||
#ifdef __SSE2__
|
||||
__m128i ai = _mm_loadl_epi64((const __m128i*)&a);
|
||||
__m128d ad = _mm_cvtepi32_pd(ai);
|
||||
__m128d bd = _mm_load_pd(&b.lo.f64);
|
||||
__m128d cd = _mm_mul_pd(ad, bd);
|
||||
__m128d mask = _mm_cmpeq_pd(cd, cd);
|
||||
cd = _mm_and_pd(cd, mask);
|
||||
_mm_store_pd(&c.lo.f64, cd);
|
||||
#else
|
||||
double alo = (double)a.i32lo;
|
||||
double ahi = (double)a.i32hi;
|
||||
c.lo.f64 = FlushNaN(alo * b.lo.f64);
|
||||
c.hi.f64 = FlushNaN(ahi * b.hi.f64);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FDIV(convertible_t& a, fpu_reg_t& b, fpu_reg_t& c) {
|
||||
#ifdef __SSE2__
|
||||
__m128i ai = _mm_loadl_epi64((const __m128i*)&a);
|
||||
__m128d ad = _mm_cvtepi32_pd(ai);
|
||||
__m128d bd = _mm_load_pd(&b.lo.f64);
|
||||
__m128d cd = _mm_div_pd(ad, bd);
|
||||
__m128d mask = _mm_cmpeq_pd(cd, cd);
|
||||
cd = _mm_and_pd(cd, mask);
|
||||
_mm_store_pd(&c.lo.f64, cd);
|
||||
#else
|
||||
double alo = (double)a.i32lo;
|
||||
double ahi = (double)a.i32hi;
|
||||
c.lo.f64 = FlushDenormalNaN(alo / b.lo.f64);
|
||||
c.hi.f64 = FlushDenormalNaN(ahi / b.hi.f64);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FSQRT(convertible_t& a, fpu_reg_t& b, fpu_reg_t& c) {
|
||||
#ifdef __SSE2__
|
||||
__m128i ai = _mm_loadl_epi64((const __m128i*)&a);
|
||||
__m128d ad = _mm_cvtepi32_pd(ai);
|
||||
const __m128d absmask = _mm_castsi128_pd(_mm_set1_epi64x(~(1LL << 63)));
|
||||
ad = _mm_and_pd(ad, absmask);
|
||||
__m128d cd = _mm_sqrt_pd(ad);
|
||||
_mm_store_pd(&c.lo.f64, cd);
|
||||
#else
|
||||
double alo = (double)a.i32lo;
|
||||
double ahi = (double)a.i32hi;
|
||||
c.lo.f64 = sqrt(std::abs(alo));
|
||||
c.hi.f64 = sqrt(std::abs(ahi));
|
||||
#endif
|
||||
}*/
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
double loadDoublePortable(const void* addr) {
|
||||
double_ser_t ds;
|
||||
ds.i = load64(addr);
|
||||
return ds.f;
|
||||
}
|
||||
|
||||
+29
-1
@@ -33,12 +33,21 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
#else
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
inline __m128d _mm_abs(__m128d xd) {
|
||||
const __m128d absmask = _mm_castsi128_pd(_mm_set1_epi64x(~(1LL << 63)));
|
||||
return _mm_and_pd(xd, absmask);
|
||||
}
|
||||
|
||||
#define PREFETCHNTA(x) _mm_prefetch((const char *)(x), _MM_HINT_NTA)
|
||||
|
||||
#else
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
|
||||
#define _mm_malloc(a,b) malloc(a)
|
||||
#define _mm_free(a) free(a)
|
||||
#define PREFETCHNTA(x)
|
||||
|
||||
typedef union {
|
||||
uint64_t u64[2];
|
||||
@@ -152,10 +161,29 @@ constexpr int RoundDown = 1;
|
||||
constexpr int RoundUp = 2;
|
||||
constexpr int RoundToZero = 3;
|
||||
|
||||
constexpr int32_t unsigned32ToSigned2sCompl(uint32_t x) {
|
||||
return (-1 == ~0) ? (int32_t)x : (x > INT32_MAX ? (-(int32_t)(UINT32_MAX - x) - 1) : (int32_t)x);
|
||||
}
|
||||
|
||||
constexpr int64_t unsigned64ToSigned2sCompl(uint64_t x) {
|
||||
return (-1 == ~0) ? (int64_t)x : (x > INT64_MAX ? (-(int64_t)(UINT64_MAX - x) - 1) : (int64_t)x);
|
||||
}
|
||||
|
||||
constexpr uint64_t signExtend2sCompl(uint32_t x) {
|
||||
return (-1 == ~0) ? (int64_t)(int32_t)(x) : (x > INT32_MAX ? (x | 0xffffffff00000000ULL) : (uint64_t)x);
|
||||
}
|
||||
|
||||
inline __m128d load_cvt_i32x2(const void* addr) {
|
||||
__m128i ix = _mm_load_si128((const __m128i*)addr);
|
||||
return _mm_cvtepi32_pd(ix);
|
||||
}
|
||||
|
||||
double loadDoublePortable(const void* addr);
|
||||
|
||||
uint64_t mulh(uint64_t, uint64_t);
|
||||
int64_t smulh(int64_t, int64_t);
|
||||
uint64_t rotl(uint64_t, int);
|
||||
uint64_t rotr(uint64_t, int);
|
||||
void initFpu();
|
||||
void setRoundMode(uint32_t);
|
||||
bool condition(uint32_t, int32_t, int32_t);
|
||||
bool condition(uint32_t, uint32_t, uint32_t);
|
||||
|
||||
+10
-9
@@ -34,7 +34,6 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
#include <atomic>
|
||||
#include "dataset.hpp"
|
||||
#include "Cache.hpp"
|
||||
#include "Pcg32.hpp"
|
||||
#include "hashAes1Rx4.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 };
|
||||
@@ -117,7 +116,7 @@ void printUsage(const char* executable) {
|
||||
}
|
||||
|
||||
void generateAsm(int nonce) {
|
||||
uint64_t hash[4];
|
||||
uint64_t hash[8];
|
||||
unsigned char blockTemplate[] = {
|
||||
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,
|
||||
@@ -128,7 +127,9 @@ void generateAsm(int nonce) {
|
||||
*noncePtr = nonce;
|
||||
blake2b(hash, sizeof(hash), blockTemplate, sizeof(blockTemplate), nullptr, 0);
|
||||
RandomX::AssemblyGeneratorX86 asmX86;
|
||||
asmX86.generateProgram(hash);
|
||||
RandomX::Program p;
|
||||
fillAes1Rx4<false>(hash, sizeof(p), &p);
|
||||
asmX86.generateProgram(p);
|
||||
asmX86.printCode(std::cout);
|
||||
}
|
||||
|
||||
@@ -143,9 +144,8 @@ void generateNative(int nonce) {
|
||||
int* noncePtr = (int*)(blockTemplate + 39);
|
||||
*noncePtr = nonce;
|
||||
blake2b(hash, sizeof(hash), blockTemplate, sizeof(blockTemplate), nullptr, 0);
|
||||
RandomX::Program prog;
|
||||
Pcg32 gen(hash);
|
||||
prog.initialize(gen);
|
||||
alignas(16) RandomX::Program prog;
|
||||
fillAes1Rx4<false>((void*)hash, sizeof(prog), &prog);
|
||||
for (int i = 0; i < RandomX::ProgramLength; ++i) {
|
||||
prog(i).dst %= 8;
|
||||
prog(i).src %= 8;
|
||||
@@ -173,12 +173,13 @@ void mine(RandomX::VirtualMachine* vm, std::atomic<int>& atomicNonce, AtomicHash
|
||||
vm->setScratchpad(scratchpad);
|
||||
//dump((char*)((RandomX::CompiledVirtualMachine*)vm)->getProgram(), RandomX::CodeSize, "code-1337-jmp.txt");
|
||||
for (int chain = 0; chain < 8; ++chain) {
|
||||
vm->initializeProgram(hash);
|
||||
fillAes1Rx4<false>((void*)hash, sizeof(RandomX::Program), vm->getProgramBuffer());
|
||||
vm->initialize();
|
||||
vm->execute();
|
||||
vm->getResult(nullptr, 0, hash);
|
||||
vm->getResult<false>(nullptr, 0, hash);
|
||||
}
|
||||
//vm->initializeProgram(hash);
|
||||
vm->getResult(scratchpad, RandomX::ScratchpadSize, hash);
|
||||
vm->getResult<false>(scratchpad, RandomX::ScratchpadSize, hash);
|
||||
result.xorWith(hash);
|
||||
if (RandomX::trace) {
|
||||
std::cout << "Nonce: " << nonce << " ";
|
||||
|
||||
+143
-138
@@ -1,17 +1,14 @@
|
||||
; FMUL_R e0, a2
|
||||
mulpd xmm4, xmm10
|
||||
; IADD_RC r2, r5, -1621224194
|
||||
; IADD_RC r2, r5, 2673743102
|
||||
lea r10, [r10+r13-1621224194]
|
||||
; ISTORE L2[r2], r7
|
||||
mov eax, r10d
|
||||
and eax, 262136
|
||||
mov qword ptr [rsi+rax], r15
|
||||
; FSUB_M f2, L1[r2]
|
||||
mov eax, r10d
|
||||
and eax, 16376
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
subpd xmm2, xmm12
|
||||
; IMUL_9C r6, -1003503212
|
||||
; FNEG_R f2
|
||||
xorps xmm2, xmm15
|
||||
; IMUL_9C r6, 3291464084
|
||||
lea r14, [r14+r14*8-1003503212]
|
||||
; FSUB_R f1, a0
|
||||
subpd xmm1, xmm8
|
||||
@@ -19,11 +16,8 @@
|
||||
mov eax, r11d
|
||||
and eax, 262136
|
||||
xor r13, qword ptr [rsi+rax]
|
||||
; FSUB_M f2, L1[r4]
|
||||
mov eax, r12d
|
||||
and eax, 16376
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
subpd xmm2, xmm12
|
||||
; FNEG_R f2
|
||||
xorps xmm2, xmm15
|
||||
; FSUB_R f3, a0
|
||||
subpd xmm3, xmm8
|
||||
; ISDIV_C r0, 1400272688
|
||||
@@ -38,35 +32,37 @@
|
||||
mov eax, r15d
|
||||
and eax, 16376
|
||||
imul r11, qword ptr [rsi+rax]
|
||||
; IROL_R r2, r3
|
||||
mov ecx, r11d
|
||||
rol r10, cl
|
||||
; ISWAP_R r2, r3
|
||||
xchg r10, r11
|
||||
; IMULH_R r6, r0
|
||||
mov rax, r14
|
||||
mul r8
|
||||
mov r14, rdx
|
||||
; FMUL_R e0, a2
|
||||
mulpd xmm4, xmm10
|
||||
; IADD_RC r3, r4, -52260428
|
||||
; IADD_RC r3, r4, 4242706868
|
||||
lea r11, [r11+r12-52260428]
|
||||
; IADD_R r7, -1138617760
|
||||
; IADD_R r7, 3156349536
|
||||
add r15, -1138617760
|
||||
; IXOR_M r2, L1[r6]
|
||||
mov eax, r14d
|
||||
and eax, 16376
|
||||
xor r10, qword ptr [rsi+rax]
|
||||
; FSUB_R f2, a1
|
||||
subpd xmm2, xmm9
|
||||
; FSUB_M f2, L1[r5]
|
||||
mov eax, r13d
|
||||
and eax, 16376
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
subpd xmm2, xmm12
|
||||
; IXOR_R r7, r1
|
||||
xor r15, r9
|
||||
; COND_R r2, lt(r7, -41618808)
|
||||
; COND_R r2, lt(r7, 4253348488)
|
||||
xor ecx, ecx
|
||||
cmp r15d, -41618808
|
||||
setl cl
|
||||
add r10, rcx
|
||||
; FMUL_R e3, a0
|
||||
mulpd xmm7, xmm8
|
||||
; COND_R r4, sg(r1, -961190365)
|
||||
; COND_R r4, sg(r1, 3333776931)
|
||||
xor ecx, ecx
|
||||
cmp r9d, -961190365
|
||||
sets cl
|
||||
@@ -122,19 +118,21 @@
|
||||
addpd xmm1, xmm8
|
||||
; FMUL_R e3, a2
|
||||
mulpd xmm7, xmm10
|
||||
; FADD_R f0, a1
|
||||
addpd xmm0, xmm9
|
||||
; FADD_M f0, L2[r5]
|
||||
mov eax, r13d
|
||||
and eax, 262136
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
addpd xmm0, xmm12
|
||||
; IMUL_R r5, r6
|
||||
imul r13, r14
|
||||
; IADD_RC r1, r2, -1263285243
|
||||
; IADD_RC r1, r2, 3031682053
|
||||
lea r9, [r9+r10-1263285243]
|
||||
; ISUB_M r4, L1[r6]
|
||||
mov eax, r14d
|
||||
and eax, 16376
|
||||
sub r12, qword ptr [rsi+rax]
|
||||
; IROL_R r7, r2
|
||||
mov ecx, r10d
|
||||
rol r15, cl
|
||||
; FSWAP_R e3
|
||||
shufpd xmm7, xmm7, 1
|
||||
; IMUL_R r0, r7
|
||||
imul r8, r15
|
||||
; IXOR_R r1, r6
|
||||
@@ -156,9 +154,8 @@
|
||||
andps xmm12, xmm14
|
||||
divpd xmm6, xmm12
|
||||
maxpd xmm6, xmm13
|
||||
; IROL_R r2, r0
|
||||
mov ecx, r8d
|
||||
rol r10, cl
|
||||
; ISWAP_R r2, r0
|
||||
xchg r10, r8
|
||||
; IADD_R r7, r5
|
||||
add r15, r13
|
||||
; FDIV_M e0, L1[r4]
|
||||
@@ -210,8 +207,11 @@
|
||||
mov eax, r8d
|
||||
and eax, 16376
|
||||
mov qword ptr [rsi+rax], r15
|
||||
; FSUB_R f0, a1
|
||||
subpd xmm0, xmm9
|
||||
; FSUB_M f0, L2[r1]
|
||||
mov eax, r9d
|
||||
and eax, 262136
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
subpd xmm0, xmm12
|
||||
; FADD_R f3, a1
|
||||
addpd xmm3, xmm9
|
||||
; IXOR_R r5, r4
|
||||
@@ -220,8 +220,8 @@
|
||||
mov eax, r15d
|
||||
and eax, 262136
|
||||
mov qword ptr [rsi+rax], r10
|
||||
; ISWAP_R r6, r7
|
||||
xchg r14, r15
|
||||
; FSWAP_R e2
|
||||
shufpd xmm6, xmm6, 1
|
||||
; FADD_R f3, a2
|
||||
addpd xmm3, xmm10
|
||||
; ISMULH_R r5, r0
|
||||
@@ -232,7 +232,7 @@
|
||||
mov eax, r12d
|
||||
and eax, 16376
|
||||
add r8, qword ptr [rsi+rax]
|
||||
; COND_R r7, ge(r6, -1972898485)
|
||||
; COND_R r7, ge(r6, 2322068811)
|
||||
xor ecx, ecx
|
||||
cmp r14d, -1972898485
|
||||
setge cl
|
||||
@@ -242,9 +242,9 @@
|
||||
; IROR_R r7, r6
|
||||
mov ecx, r14d
|
||||
ror r15, cl
|
||||
; IADD_RC r2, r4, -117457973
|
||||
; IADD_RC r2, r4, 4177509323
|
||||
lea r10, [r10+r12-117457973]
|
||||
; IMUL_R r0, -1500893068
|
||||
; IMUL_R r0, 2794074228
|
||||
imul r8, -1500893068
|
||||
; IADD_R r2, r3
|
||||
add r10, r11
|
||||
@@ -265,19 +265,19 @@
|
||||
lea r14, [r14+r14+540663146]
|
||||
; IROR_R r5, 58
|
||||
ror r13, 58
|
||||
; FSWAP_R f2
|
||||
shufpd xmm2, xmm2, 1
|
||||
; FSWAP_R f2
|
||||
shufpd xmm2, xmm2, 1
|
||||
; FADD_R f2, a1
|
||||
addpd xmm2, xmm9
|
||||
; FADD_R f2, a2
|
||||
addpd xmm2, xmm10
|
||||
; FMUL_R e1, a2
|
||||
mulpd xmm5, xmm10
|
||||
; ISWAP_R r5, r6
|
||||
xchg r13, r14
|
||||
; FSWAP_R e1
|
||||
shufpd xmm5, xmm5, 1
|
||||
; IADD_R r5, r3
|
||||
add r13, r11
|
||||
; IADD_R r7, -1780268176
|
||||
; IADD_R r7, 2514699120
|
||||
add r15, -1780268176
|
||||
; IADD_RC r7, r0, -1497756854
|
||||
; IADD_RC r7, r0, 2797210442
|
||||
lea r15, [r15+r8-1497756854]
|
||||
; ISTORE L2[r0], r7
|
||||
mov eax, r8d
|
||||
@@ -287,8 +287,11 @@
|
||||
mov rax, r10
|
||||
imul r12
|
||||
mov r10, rdx
|
||||
; FSUB_R f0, a2
|
||||
subpd xmm0, xmm10
|
||||
; FSUB_M f0, L1[r2]
|
||||
mov eax, r10d
|
||||
and eax, 16376
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
subpd xmm0, xmm12
|
||||
; ISMULH_R r2, r3
|
||||
mov rax, r10
|
||||
imul r11
|
||||
@@ -301,8 +304,11 @@
|
||||
addpd xmm2, xmm8
|
||||
; FMUL_R e0, a2
|
||||
mulpd xmm4, xmm10
|
||||
; FADD_R f2, a3
|
||||
addpd xmm2, xmm11
|
||||
; FADD_M f2, L1[r3]
|
||||
mov eax, r11d
|
||||
and eax, 16376
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
addpd xmm2, xmm12
|
||||
; IMUL_R r1, r2
|
||||
imul r9, r10
|
||||
; IMUL_M r7, L1[r5]
|
||||
@@ -313,11 +319,8 @@
|
||||
imul r11, r10
|
||||
; IXOR_R r1, r0
|
||||
xor r9, r8
|
||||
; FSUB_M f0, L1[r5]
|
||||
mov eax, r13d
|
||||
and eax, 16376
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
subpd xmm0, xmm12
|
||||
; FNEG_R f0
|
||||
xorps xmm0, xmm15
|
||||
; IADD_RC r4, r4, 1456841848
|
||||
lea r12, [r12+r12+1456841848]
|
||||
; IXOR_R r3, r2
|
||||
@@ -327,19 +330,16 @@
|
||||
cmp r12d, 1678513610
|
||||
seto cl
|
||||
add r8, rcx
|
||||
; ISMULH_R r4, -1620573087
|
||||
mov rax, -1620573087
|
||||
; ISMULH_R r4, r4
|
||||
mov rax, r12
|
||||
imul r12
|
||||
add r12, rdx
|
||||
mov r12, rdx
|
||||
; IMUL_R r4, r1
|
||||
imul r12, r9
|
||||
; FSWAP_R e1
|
||||
shufpd xmm5, xmm5, 1
|
||||
; FADD_M f2, L1[r0]
|
||||
mov eax, r8d
|
||||
and eax, 16376
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
addpd xmm2, xmm12
|
||||
; FADD_R f1, a2
|
||||
addpd xmm1, xmm10
|
||||
; FSUB_R f2, a0
|
||||
subpd xmm2, xmm8
|
||||
; FMUL_R e1, a2
|
||||
mulpd xmm5, xmm10
|
||||
; FSUB_R f0, a3
|
||||
@@ -362,29 +362,35 @@
|
||||
sub r12, qword ptr [rsi+rax]
|
||||
; FADD_R f2, a2
|
||||
addpd xmm2, xmm10
|
||||
; FSUB_R f3, a0
|
||||
subpd xmm3, xmm8
|
||||
; FSUB_M f3, L2[r4]
|
||||
mov eax, r12d
|
||||
and eax, 262136
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
subpd xmm3, xmm12
|
||||
; IXOR_R r7, r2
|
||||
xor r15, r10
|
||||
; IXOR_R r0, r5
|
||||
xor r8, r13
|
||||
; FSWAP_R f1
|
||||
shufpd xmm1, xmm1, 1
|
||||
; FADD_R f1, a2
|
||||
addpd xmm1, xmm10
|
||||
; FMUL_R e3, a2
|
||||
mulpd xmm7, xmm10
|
||||
; ISWAP_R r7, r1
|
||||
xchg r15, r9
|
||||
; ISWAP_R r1, r4
|
||||
xchg r9, r12
|
||||
; COND_R r2, ge(r2, -226330940)
|
||||
; FSWAP_R e3
|
||||
shufpd xmm7, xmm7, 1
|
||||
; FSWAP_R f1
|
||||
shufpd xmm1, xmm1, 1
|
||||
; COND_R r2, ge(r2, 4068636356)
|
||||
xor ecx, ecx
|
||||
cmp r10d, -226330940
|
||||
setge cl
|
||||
add r10, rcx
|
||||
; FMUL_R e2, a3
|
||||
mulpd xmm6, xmm11
|
||||
; FSUB_R f2, a1
|
||||
subpd xmm2, xmm9
|
||||
; FSUB_M f2, L2[r1]
|
||||
mov eax, r9d
|
||||
and eax, 262136
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
subpd xmm2, xmm12
|
||||
; FADD_R f1, a0
|
||||
addpd xmm1, xmm8
|
||||
; ISUB_R r7, r5
|
||||
@@ -395,12 +401,11 @@
|
||||
sub r8, qword ptr [rsi+rax]
|
||||
; FSUB_R f3, a1
|
||||
subpd xmm3, xmm9
|
||||
; IROL_R r3, r5
|
||||
mov ecx, r13d
|
||||
rol r11, cl
|
||||
; ISWAP_R r3, r5
|
||||
xchg r11, r13
|
||||
; IADD_RC r5, r2, 795784298
|
||||
lea r13, [r13+r10+795784298]
|
||||
; IADD_RC r0, r4, -2050178553
|
||||
; IADD_RC r0, r4, 2244788743
|
||||
lea r8, [r8+r12-2050178553]
|
||||
; IMUL_9C r5, 1062534001
|
||||
lea r13, [r13+r13*8+1062534001]
|
||||
@@ -436,16 +441,15 @@
|
||||
mov rax, r12
|
||||
imul r10
|
||||
mov r12, rdx
|
||||
; IROL_R r3, r0
|
||||
mov ecx, r8d
|
||||
rol r11, cl
|
||||
; ISWAP_R r3, r0
|
||||
xchg r11, r8
|
||||
; IXOR_R r2, r0
|
||||
xor r10, r8
|
||||
; IXOR_M r0, L2[r1]
|
||||
mov eax, r9d
|
||||
and eax, 262136
|
||||
xor r8, qword ptr [rsi+rax]
|
||||
; ISDIV_C r7, -935446980
|
||||
; ISDIV_C r7, 3359520316
|
||||
mov rax, 7859804860668271393
|
||||
imul r15
|
||||
xor eax, eax
|
||||
@@ -458,11 +462,8 @@
|
||||
mov eax, r10d
|
||||
and eax, 16376
|
||||
imul r14, qword ptr [rsi+rax]
|
||||
; FSUB_M f3, L1[r6]
|
||||
mov eax, r14d
|
||||
and eax, 16376
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
subpd xmm3, xmm12
|
||||
; FNEG_R f3
|
||||
xorps xmm3, xmm15
|
||||
; IADD_RC r4, r2, 1704868083
|
||||
lea r12, [r12+r10+1704868083]
|
||||
; FADD_R f2, a0
|
||||
@@ -471,8 +472,11 @@
|
||||
mov eax, r8d
|
||||
and eax, 16376
|
||||
mov qword ptr [rsi+rax], r8
|
||||
; FADD_R f0, a3
|
||||
addpd xmm0, xmm11
|
||||
; FADD_M f0, L1[r7]
|
||||
mov eax, r15d
|
||||
and eax, 16376
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
addpd xmm0, xmm12
|
||||
; FMUL_R e0, a3
|
||||
mulpd xmm4, xmm11
|
||||
; FSUB_R f3, a2
|
||||
@@ -481,8 +485,8 @@
|
||||
lea r15, [r15+r15+1302457878]
|
||||
; ISUB_R r1, 1330165941
|
||||
sub r9, 1330165941
|
||||
; FSUB_R f1, a3
|
||||
subpd xmm1, xmm11
|
||||
; FNEG_R f1
|
||||
xorps xmm1, xmm15
|
||||
; IROR_R r0, r4
|
||||
mov ecx, r12d
|
||||
ror r8, cl
|
||||
@@ -491,7 +495,7 @@
|
||||
; IROR_R r5, r6
|
||||
mov ecx, r14d
|
||||
ror r13, cl
|
||||
; COND_R r0, ab(r1, -310933871)
|
||||
; COND_R r0, ab(r1, 3984033425)
|
||||
xor ecx, ecx
|
||||
cmp r9d, -310933871
|
||||
seta cl
|
||||
@@ -516,22 +520,22 @@
|
||||
andps xmm12, xmm14
|
||||
divpd xmm5, xmm12
|
||||
maxpd xmm5, xmm13
|
||||
; IROL_R r1, 5
|
||||
rol r9, 5
|
||||
; IADD_R r7, -1421188024
|
||||
; FSWAP_R f1
|
||||
shufpd xmm1, xmm1, 1
|
||||
; IADD_R r7, 2873779272
|
||||
add r15, -1421188024
|
||||
; FSUB_R f3, a2
|
||||
subpd xmm3, xmm10
|
||||
; FSUB_M f3, L2[r2]
|
||||
mov eax, r10d
|
||||
and eax, 262136
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
subpd xmm3, xmm12
|
||||
; FSUB_R f2, a3
|
||||
subpd xmm2, xmm11
|
||||
; FADD_M f3, L1[r1]
|
||||
mov eax, r9d
|
||||
and eax, 16376
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
addpd xmm3, xmm12
|
||||
; FSUB_R f3, a1
|
||||
subpd xmm3, xmm9
|
||||
; FMUL_R e1, a3
|
||||
mulpd xmm5, xmm11
|
||||
; IADD_RC r2, r4, -317832028
|
||||
; IADD_RC r2, r4, 3977135268
|
||||
lea r10, [r10+r12-317832028]
|
||||
; IMUL_M r4, L1[r5]
|
||||
mov eax, r13d
|
||||
@@ -575,12 +579,11 @@
|
||||
sub r12, r9
|
||||
; ISUB_R r3, r0
|
||||
sub r11, r8
|
||||
; IROL_R r7, r5
|
||||
mov ecx, r13d
|
||||
rol r15, cl
|
||||
; ISWAP_R r7, r5
|
||||
xchg r15, r13
|
||||
; IMUL_R r2, r6
|
||||
imul r10, r14
|
||||
; COND_R r2, ge(r2, -1892157506)
|
||||
; COND_R r2, ge(r2, 2402809790)
|
||||
xor ecx, ecx
|
||||
cmp r10d, -1892157506
|
||||
setge cl
|
||||
@@ -596,7 +599,7 @@
|
||||
add r9, rdx
|
||||
; FADD_R f0, a1
|
||||
addpd xmm0, xmm9
|
||||
; IADD_RC r5, r7, -477591118
|
||||
; IADD_RC r5, r7, 3817376178
|
||||
lea r13, [r13+r15-477591118]
|
||||
; FSUB_R f0, a3
|
||||
subpd xmm0, xmm11
|
||||
@@ -610,9 +613,12 @@
|
||||
add r8, r12
|
||||
; FSUB_R f3, a1
|
||||
subpd xmm3, xmm9
|
||||
; FSUB_R f2, a0
|
||||
subpd xmm2, xmm8
|
||||
; ISDIV_C r2, -396711688
|
||||
; FSUB_M f2, L1[r4]
|
||||
mov eax, r12d
|
||||
and eax, 16376
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
subpd xmm2, xmm12
|
||||
; ISDIV_C r2, 3898255608
|
||||
mov rax, 5964731804029407733
|
||||
imul r10
|
||||
xor eax, eax
|
||||
@@ -621,16 +627,19 @@
|
||||
sets al
|
||||
add rdx, rax
|
||||
add r10, rdx
|
||||
; FSUB_R f2, a2
|
||||
subpd xmm2, xmm10
|
||||
; FNEG_R f2
|
||||
xorps xmm2, xmm15
|
||||
; FSUB_R f3, a2
|
||||
subpd xmm3, xmm10
|
||||
; FADD_R f1, a3
|
||||
addpd xmm1, xmm11
|
||||
; IMUL_R r3, r2
|
||||
imul r11, r10
|
||||
; FADD_R f0, a3
|
||||
addpd xmm0, xmm11
|
||||
; FADD_M f0, L1[r3]
|
||||
mov eax, r11d
|
||||
and eax, 16376
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
addpd xmm0, xmm12
|
||||
; ISMULH_R r5, r2
|
||||
mov rax, r13
|
||||
imul r10
|
||||
@@ -639,28 +648,30 @@
|
||||
mov rax, r14
|
||||
mul r10
|
||||
mov r14, rdx
|
||||
; FADD_R f3, a3
|
||||
addpd xmm3, xmm11
|
||||
; FADD_M f3, L1[r3]
|
||||
mov eax, r11d
|
||||
and eax, 16376
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
addpd xmm3, xmm12
|
||||
; IMUL_R r6, r7
|
||||
imul r14, r15
|
||||
; FSUB_R f0, a0
|
||||
subpd xmm0, xmm8
|
||||
; FSUB_R f2, a0
|
||||
subpd xmm2, xmm8
|
||||
; FNEG_R f2
|
||||
xorps xmm2, xmm15
|
||||
; ISUB_R r6, r4
|
||||
sub r14, r12
|
||||
; FSWAP_R f1
|
||||
shufpd xmm1, xmm1, 1
|
||||
; FADD_R f1, a1
|
||||
addpd xmm1, xmm9
|
||||
; IXOR_R r0, r5
|
||||
xor r8, r13
|
||||
; FADD_R f2, a1
|
||||
addpd xmm2, xmm9
|
||||
; IROL_R r7, r5
|
||||
mov ecx, r13d
|
||||
rol r15, cl
|
||||
; ISWAP_R r7, r5
|
||||
xchg r15, r13
|
||||
; FMUL_R e3, a2
|
||||
mulpd xmm7, xmm10
|
||||
; IADD_RC r3, r6, -1317630728
|
||||
; IADD_RC r3, r6, 2977336568
|
||||
lea r11, [r11+r14-1317630728]
|
||||
; IMUL_R r2, r3
|
||||
imul r10, r11
|
||||
@@ -668,11 +679,8 @@
|
||||
lea r9, [r9+r12+894105694]
|
||||
; IMUL_9C r7, 504293473
|
||||
lea r15, [r15+r15*8+504293473]
|
||||
; FADD_M f1, L2[r0]
|
||||
mov eax, r8d
|
||||
and eax, 262136
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
addpd xmm1, xmm12
|
||||
; FSUB_R f1, a0
|
||||
subpd xmm1, xmm8
|
||||
; IMUL_R r7, r1
|
||||
imul r15, r9
|
||||
; IXOR_R r2, r4
|
||||
@@ -713,19 +721,16 @@
|
||||
mov eax, r9d
|
||||
and eax, 16376
|
||||
mov qword ptr [rsi+rax], r13
|
||||
; FSUB_M f0, L1[r1]
|
||||
mov eax, r9d
|
||||
and eax, 16376
|
||||
cvtdq2pd xmm12, qword ptr [rsi+rax]
|
||||
subpd xmm0, xmm12
|
||||
; FNEG_R f0
|
||||
xorps xmm0, xmm15
|
||||
; FSQRT_R e2
|
||||
sqrtpd xmm6, xmm6
|
||||
; FMUL_R e0, a3
|
||||
mulpd xmm4, xmm11
|
||||
; FMUL_R e3, a2
|
||||
mulpd xmm7, xmm10
|
||||
; IROL_R r5, r2
|
||||
; IROR_R r5, r2
|
||||
mov ecx, r10d
|
||||
rol r13, cl
|
||||
ror r13, cl
|
||||
; IADD_R r0, r4
|
||||
add r8, r12
|
||||
|
||||
@@ -17,6 +17,11 @@ You should have received a copy of the GNU General Public License
|
||||
along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
Based on the original idea by SChernykh:
|
||||
https://github.com/SChernykh/xmr-stak-cpu/issues/1#issuecomment-414336613
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if !defined(_M_X64) && !defined(__x86_64__)
|
||||
|
||||
-723
@@ -1,723 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2018 Positive Technologies, https://www.ptsecurity.com,
|
||||
* Fast Positive Hash.
|
||||
*
|
||||
* Portions Copyright (c) 2010-2018 Leonid Yuriev <leo@yuriev.ru>,
|
||||
* The 1Hippeus project (t1h).
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgement in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
* t1ha = { Fast Positive Hash, aka "Позитивный Хэш" }
|
||||
* by [Positive Technologies](https://www.ptsecurity.ru)
|
||||
*
|
||||
* Briefly, it is a 64-bit Hash Function:
|
||||
* 1. Created for 64-bit little-endian platforms, in predominantly for x86_64,
|
||||
* but portable and without penalties it can run on any 64-bit CPU.
|
||||
* 2. In most cases up to 15% faster than City64, xxHash, mum-hash, metro-hash
|
||||
* and all others portable hash-functions (which do not use specific
|
||||
* hardware tricks).
|
||||
* 3. Not suitable for cryptography.
|
||||
*
|
||||
* The Future will Positive. Всё будет хорошо.
|
||||
*
|
||||
* ACKNOWLEDGEMENT:
|
||||
* The t1ha was originally developed by Leonid Yuriev (Леонид Юрьев)
|
||||
* for The 1Hippeus project - zerocopy messaging in the spirit of Sparta!
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* PLEASE PAY ATTENTION TO THE FOLLOWING NOTES
|
||||
* about macros definitions which controls t1ha behaviour and/or performance.
|
||||
*
|
||||
*
|
||||
* 1) T1HA_SYS_UNALIGNED_ACCESS = Defines the system/platform/CPU/architecture
|
||||
* abilities for unaligned data access.
|
||||
*
|
||||
* By default, when the T1HA_SYS_UNALIGNED_ACCESS not defined,
|
||||
* it will defined on the basis hardcoded knowledge about of capabilities
|
||||
* of most common CPU architectures. But you could override this
|
||||
* default behavior when build t1ha library itself:
|
||||
*
|
||||
* // To disable unaligned access at all.
|
||||
* #define T1HA_SYS_UNALIGNED_ACCESS 0
|
||||
*
|
||||
* // To enable unaligned access, but indicate that it significally slow.
|
||||
* #define T1HA_SYS_UNALIGNED_ACCESS 1
|
||||
*
|
||||
* // To enable unaligned access, and indicate that it effecient.
|
||||
* #define T1HA_SYS_UNALIGNED_ACCESS 2
|
||||
*
|
||||
*
|
||||
* 2) T1HA_USE_FAST_ONESHOT_READ = Controls the data reads at the end of buffer.
|
||||
*
|
||||
* When defined to non-zero, t1ha will use 'one shot' method for reading
|
||||
* up to 8 bytes at the end of data. In this case just the one 64-bit read
|
||||
* will be performed even when the available less than 8 bytes.
|
||||
*
|
||||
* This is little bit faster that switching by length of data tail.
|
||||
* Unfortunately this will triggering a false-positive alarms from Valgrind,
|
||||
* AddressSanitizer and other similar tool.
|
||||
*
|
||||
* By default, t1ha defines it to 1, but you could override this
|
||||
* default behavior when build t1ha library itself:
|
||||
*
|
||||
* // For little bit faster and small code.
|
||||
* #define T1HA_USE_FAST_ONESHOT_READ 1
|
||||
*
|
||||
* // For calmness if doubt.
|
||||
* #define T1HA_USE_FAST_ONESHOT_READ 0
|
||||
*
|
||||
*
|
||||
* 3) T1HA0_RUNTIME_SELECT = Controls choice fastest function in runtime.
|
||||
*
|
||||
* t1ha library offers the t1ha0() function as the fastest for current CPU.
|
||||
* But actual CPU's features/capabilities and may be significantly different,
|
||||
* especially on x86 platform. Therefore, internally, t1ha0() may require
|
||||
* dynamic dispatching for choice best implementation.
|
||||
*
|
||||
* By default, t1ha enables such runtime choice and (may be) corresponding
|
||||
* indirect calls if it reasonable, but you could override this default
|
||||
* behavior when build t1ha library itself:
|
||||
*
|
||||
* // To enable runtime choice of fastest implementation.
|
||||
* #define T1HA0_RUNTIME_SELECT 1
|
||||
*
|
||||
* // To disable runtime choice of fastest implementation.
|
||||
* #define T1HA0_RUNTIME_SELECT 0
|
||||
*
|
||||
* When T1HA0_RUNTIME_SELECT is nonzero the t1ha0_resolve() function could
|
||||
* be used to get actual t1ha0() implementation address at runtime. This is
|
||||
* useful for two cases:
|
||||
* - calling by local pointer-to-function usually is little
|
||||
* bit faster (less overhead) than via a PLT thru the DSO boundary.
|
||||
* - GNU Indirect functions (see below) don't supported by environment
|
||||
* and calling by t1ha0_funcptr is not available and/or expensive.
|
||||
*
|
||||
* 4) T1HA_USE_INDIRECT_FUNCTIONS = Controls usage of GNU Indirect functions.
|
||||
*
|
||||
* In continue of T1HA0_RUNTIME_SELECT the T1HA_USE_INDIRECT_FUNCTIONS
|
||||
* controls usage of ELF indirect functions feature. In general, when
|
||||
* available, this reduces overhead of indirect function's calls though
|
||||
* a DSO-bundary (https://sourceware.org/glibc/wiki/GNU_IFUNC).
|
||||
*
|
||||
* By default, t1ha engage GNU Indirect functions when it available
|
||||
* and useful, but you could override this default behavior when build
|
||||
* t1ha library itself:
|
||||
*
|
||||
* // To enable use of GNU ELF Indirect functions.
|
||||
* #define T1HA_USE_INDIRECT_FUNCTIONS 1
|
||||
*
|
||||
* // To disable use of GNU ELF Indirect functions. This may be useful
|
||||
* // if the actual toolchain or the system's loader don't support ones.
|
||||
* #define T1HA_USE_INDIRECT_FUNCTIONS 0
|
||||
*
|
||||
* 5) T1HA0_AESNI_AVAILABLE = Controls AES-NI detection and dispatching on x86.
|
||||
*
|
||||
* In continue of T1HA0_RUNTIME_SELECT the T1HA0_AESNI_AVAILABLE controls
|
||||
* detection and usage of AES-NI CPU's feature. On the other hand, this
|
||||
* requires compiling parts of t1ha library with certain properly options,
|
||||
* and could be difficult or inconvenient in some cases.
|
||||
*
|
||||
* By default, t1ha engade AES-NI for t1ha0() on the x86 platform, but
|
||||
* you could override this default behavior when build t1ha library itself:
|
||||
*
|
||||
* // To disable detection and usage of AES-NI instructions for t1ha0().
|
||||
* // This may be useful when you unable to build t1ha library properly
|
||||
* // or known that AES-NI will be unavailable at the deploy.
|
||||
* #define T1HA0_AESNI_AVAILABLE 0
|
||||
*
|
||||
* // To force detection and usage of AES-NI instructions for t1ha0(),
|
||||
* // but I don't known reasons to anybody would need this.
|
||||
* #define T1HA0_AESNI_AVAILABLE 1
|
||||
*
|
||||
* 6) T1HA0_DISABLED, T1HA1_DISABLED, T1HA2_DISABLED = Controls availability of
|
||||
* t1ha functions.
|
||||
*
|
||||
* In some cases could be useful to import/use only few of t1ha functions
|
||||
* or just the one. So, this definitions allows disable corresponding parts
|
||||
* of t1ha library.
|
||||
*
|
||||
* // To disable t1ha0(), t1ha0_32le(), t1ha0_32be() and all AES-NI.
|
||||
* #define T1HA0_DISABLED
|
||||
*
|
||||
* // To disable t1ha1_le() and t1ha1_be().
|
||||
* #define T1HA1_DISABLED
|
||||
*
|
||||
* // To disable t1ha2_atonce(), t1ha2_atonce128() and so on.
|
||||
* #define T1HA2_DISABLED
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
#define T1HA_VERSION_MAJOR 2
|
||||
#define T1HA_VERSION_MINOR 1
|
||||
#define T1HA_VERSION_RELEASE 0
|
||||
|
||||
#ifndef __has_attribute
|
||||
#define __has_attribute(x) (0)
|
||||
#endif
|
||||
|
||||
#ifndef __has_include
|
||||
#define __has_include(x) (0)
|
||||
#endif
|
||||
|
||||
#ifndef __GNUC_PREREQ
|
||||
#if defined(__GNUC__) && defined(__GNUC_MINOR__)
|
||||
#define __GNUC_PREREQ(maj, min) \
|
||||
((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
|
||||
#else
|
||||
#define __GNUC_PREREQ(maj, min) 0
|
||||
#endif
|
||||
#endif /* __GNUC_PREREQ */
|
||||
|
||||
#ifndef __CLANG_PREREQ
|
||||
#ifdef __clang__
|
||||
#define __CLANG_PREREQ(maj, min) \
|
||||
((__clang_major__ << 16) + __clang_minor__ >= ((maj) << 16) + (min))
|
||||
#else
|
||||
#define __CLANG_PREREQ(maj, min) (0)
|
||||
#endif
|
||||
#endif /* __CLANG_PREREQ */
|
||||
|
||||
#ifndef __LCC_PREREQ
|
||||
#ifdef __LCC__
|
||||
#define __LCC_PREREQ(maj, min) \
|
||||
((__LCC__ << 16) + __LCC_MINOR__ >= ((maj) << 16) + (min))
|
||||
#else
|
||||
#define __LCC_PREREQ(maj, min) (0)
|
||||
#endif
|
||||
#endif /* __LCC_PREREQ */
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#ifdef _MSC_VER
|
||||
/* Avoid '16' bytes padding added after data member 't1ha_context::total'
|
||||
* and other warnings from std-headers if warning-level > 3. */
|
||||
#pragma warning(push, 3)
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus) && __cplusplus >= 201103L
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#else
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#if defined(i386) || defined(__386) || defined(__i386) || defined(__i386__) || \
|
||||
defined(i486) || defined(__i486) || defined(__i486__) || \
|
||||
defined(i586) | defined(__i586) || defined(__i586__) || defined(i686) || \
|
||||
defined(__i686) || defined(__i686__) || defined(_M_IX86) || \
|
||||
defined(_X86_) || defined(__THW_INTEL__) || defined(__I86__) || \
|
||||
defined(__INTEL__) || defined(__x86_64) || defined(__x86_64__) || \
|
||||
defined(__amd64__) || defined(__amd64) || defined(_M_X64) || \
|
||||
defined(_M_AMD64) || defined(__IA32__) || defined(__INTEL__)
|
||||
#ifndef __ia32__
|
||||
/* LY: define neutral __ia32__ for x86 and x86-64 archs */
|
||||
#define __ia32__ 1
|
||||
#endif /* __ia32__ */
|
||||
#if !defined(__amd64__) && (defined(__x86_64) || defined(__x86_64__) || \
|
||||
defined(__amd64) || defined(_M_X64))
|
||||
/* LY: define trusty __amd64__ for all AMD64/x86-64 arch */
|
||||
#define __amd64__ 1
|
||||
#endif /* __amd64__ */
|
||||
#endif /* all x86 */
|
||||
|
||||
#if !defined(__BYTE_ORDER__) || !defined(__ORDER_LITTLE_ENDIAN__) || \
|
||||
!defined(__ORDER_BIG_ENDIAN__)
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
/* clang-format off */
|
||||
|
||||
#if defined(__GLIBC__) || defined(__GNU_LIBRARY__) || defined(__ANDROID__) || \
|
||||
defined(HAVE_ENDIAN_H) || __has_include(<endian.h>)
|
||||
#include <endian.h>
|
||||
#elif defined(__APPLE__) || defined(__MACH__) || defined(__OpenBSD__) || \
|
||||
defined(HAVE_MACHINE_ENDIAN_H) || __has_include(<machine/endian.h>)
|
||||
#include <machine/endian.h>
|
||||
#elif defined(HAVE_SYS_ISA_DEFS_H) || __has_include(<sys/isa_defs.h>)
|
||||
#include <sys/isa_defs.h>
|
||||
#elif (defined(HAVE_SYS_TYPES_H) && defined(HAVE_SYS_ENDIAN_H)) || \
|
||||
(__has_include(<sys/types.h>) && __has_include(<sys/endian.h>))
|
||||
#include <sys/endian.h>
|
||||
#include <sys/types.h>
|
||||
#elif defined(__bsdi__) || defined(__DragonFly__) || defined(__FreeBSD__) || \
|
||||
defined(__NETBSD__) || defined(__NetBSD__) || \
|
||||
defined(HAVE_SYS_PARAM_H) || __has_include(<sys/param.h>)
|
||||
#include <sys/param.h>
|
||||
#endif /* OS */
|
||||
|
||||
/* *INDENT-ON* */
|
||||
/* clang-format on */
|
||||
|
||||
#if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && defined(__BIG_ENDIAN)
|
||||
#define __ORDER_LITTLE_ENDIAN__ __LITTLE_ENDIAN
|
||||
#define __ORDER_BIG_ENDIAN__ __BIG_ENDIAN
|
||||
#define __BYTE_ORDER__ __BYTE_ORDER
|
||||
#elif defined(_BYTE_ORDER) && defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN)
|
||||
#define __ORDER_LITTLE_ENDIAN__ _LITTLE_ENDIAN
|
||||
#define __ORDER_BIG_ENDIAN__ _BIG_ENDIAN
|
||||
#define __BYTE_ORDER__ _BYTE_ORDER
|
||||
#else
|
||||
#define __ORDER_LITTLE_ENDIAN__ 1234
|
||||
#define __ORDER_BIG_ENDIAN__ 4321
|
||||
|
||||
#if defined(__LITTLE_ENDIAN__) || \
|
||||
(defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)) || \
|
||||
defined(__ARMEL__) || defined(__THUMBEL__) || defined(__AARCH64EL__) || \
|
||||
defined(__MIPSEL__) || defined(_MIPSEL) || defined(__MIPSEL) || \
|
||||
defined(_M_ARM) || defined(_M_ARM64) || defined(__e2k__) || \
|
||||
defined(__elbrus_4c__) || defined(__elbrus_8c__) || defined(__bfin__) || \
|
||||
defined(__BFIN__) || defined(__ia64__) || defined(_IA64) || \
|
||||
defined(__IA64__) || defined(__ia64) || defined(_M_IA64) || \
|
||||
defined(__itanium__) || defined(__ia32__) || defined(__CYGWIN__) || \
|
||||
defined(_WIN64) || defined(_WIN32) || defined(__TOS_WIN__) || \
|
||||
defined(__WINDOWS__)
|
||||
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
|
||||
|
||||
#elif defined(__BIG_ENDIAN__) || \
|
||||
(defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)) || \
|
||||
defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \
|
||||
defined(__MIPSEB__) || defined(_MIPSEB) || defined(__MIPSEB) || \
|
||||
defined(__m68k__) || defined(M68000) || defined(__hppa__) || \
|
||||
defined(__hppa) || defined(__HPPA__) || defined(__sparc__) || \
|
||||
defined(__sparc) || defined(__370__) || defined(__THW_370__) || \
|
||||
defined(__s390__) || defined(__s390x__) || defined(__SYSC_ZARCH__)
|
||||
#define __BYTE_ORDER__ __ORDER_BIG_ENDIAN__
|
||||
|
||||
#else
|
||||
#error __BYTE_ORDER__ should be defined.
|
||||
#endif /* Arch */
|
||||
|
||||
#endif
|
||||
#endif /* __BYTE_ORDER__ || __ORDER_LITTLE_ENDIAN__ || __ORDER_BIG_ENDIAN__ */
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#ifndef __dll_export
|
||||
#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
|
||||
#if defined(__GNUC__) || __has_attribute(dllexport)
|
||||
#define __dll_export __attribute__((dllexport))
|
||||
#elif defined(_MSC_VER)
|
||||
#define __dll_export __declspec(dllexport)
|
||||
#else
|
||||
#define __dll_export
|
||||
#endif
|
||||
#elif defined(__GNUC__) || __has_attribute(visibility)
|
||||
#define __dll_export __attribute__((visibility("default")))
|
||||
#else
|
||||
#define __dll_export
|
||||
#endif
|
||||
#endif /* __dll_export */
|
||||
|
||||
#ifndef __dll_import
|
||||
#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
|
||||
#if defined(__GNUC__) || __has_attribute(dllimport)
|
||||
#define __dll_import __attribute__((dllimport))
|
||||
#elif defined(_MSC_VER)
|
||||
#define __dll_import __declspec(dllimport)
|
||||
#else
|
||||
#define __dll_import
|
||||
#endif
|
||||
#else
|
||||
#define __dll_import
|
||||
#endif
|
||||
#endif /* __dll_import */
|
||||
|
||||
#ifndef __force_inline
|
||||
#ifdef _MSC_VER
|
||||
#define __force_inline __forceinline
|
||||
#elif __GNUC_PREREQ(3, 2) || __has_attribute(always_inline)
|
||||
#define __force_inline __inline __attribute__((always_inline))
|
||||
#else
|
||||
#define __force_inline __inline
|
||||
#endif
|
||||
#endif /* __force_inline */
|
||||
|
||||
#ifndef T1HA_API
|
||||
#if defined(t1ha_EXPORTS)
|
||||
#define T1HA_API __dll_export
|
||||
#elif defined(t1ha_IMPORTS)
|
||||
#define T1HA_API __dll_import
|
||||
#else
|
||||
#define T1HA_API
|
||||
#endif
|
||||
#endif /* T1HA_API */
|
||||
|
||||
#if defined(_MSC_VER) && defined(__ia32__)
|
||||
#define T1HA_ALIGN_PREFIX __declspec(align(32)) /* required only for SIMD */
|
||||
#else
|
||||
#define T1HA_ALIGN_PREFIX
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
#if defined(__GNUC__) && defined(__ia32__)
|
||||
#define T1HA_ALIGN_SUFFIX \
|
||||
__attribute__((aligned(32))) /* required only for SIMD */
|
||||
#else
|
||||
#define T1HA_ALIGN_SUFFIX
|
||||
#endif /* GCC x86 */
|
||||
|
||||
#ifndef T1HA_USE_INDIRECT_FUNCTIONS
|
||||
/* GNU ELF indirect functions usage control. For more info please see
|
||||
* https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
|
||||
* and https://sourceware.org/glibc/wiki/GNU_IFUNC */
|
||||
#if __has_attribute(ifunc) && \
|
||||
defined(__ELF__) /* ifunc is broken on Darwin/OSX */
|
||||
/* Use ifunc/gnu_indirect_function if corresponding attribute is available,
|
||||
* Assuming compiler will generate properly code even when
|
||||
* the -fstack-protector-all and/or the -fsanitize=address are enabled. */
|
||||
#define T1HA_USE_INDIRECT_FUNCTIONS 1
|
||||
#elif defined(__ELF__) && !defined(__SANITIZE_ADDRESS__) && \
|
||||
!defined(__SSP_ALL__)
|
||||
/* ifunc/gnu_indirect_function will be used on ELF, but only if both
|
||||
* -fstack-protector-all and -fsanitize=address are NOT enabled. */
|
||||
#define T1HA_USE_INDIRECT_FUNCTIONS 1
|
||||
#else
|
||||
#define T1HA_USE_INDIRECT_FUNCTIONS 0
|
||||
#endif
|
||||
#endif /* T1HA_USE_INDIRECT_FUNCTIONS */
|
||||
|
||||
#if __GNUC_PREREQ(4, 0)
|
||||
#pragma GCC visibility push(hidden)
|
||||
#endif /* __GNUC_PREREQ(4,0) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef union T1HA_ALIGN_PREFIX t1ha_state256 {
|
||||
uint8_t bytes[32];
|
||||
uint32_t u32[8];
|
||||
uint64_t u64[4];
|
||||
struct {
|
||||
uint64_t a, b, c, d;
|
||||
} n;
|
||||
} t1ha_state256_t T1HA_ALIGN_SUFFIX;
|
||||
|
||||
typedef struct t1ha_context {
|
||||
t1ha_state256_t state;
|
||||
t1ha_state256_t buffer;
|
||||
size_t partial;
|
||||
uint64_t total;
|
||||
} t1ha_context_t;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Self-testing API.
|
||||
*
|
||||
* Unfortunately, some compilers (exactly only Microsoft Visual C/C++) has
|
||||
* a bugs which leads t1ha-functions to produce wrong results. This API allows
|
||||
* check the correctness of the actual code in runtime.
|
||||
*
|
||||
* All check-functions returns 0 on success, or -1 in case the corresponding
|
||||
* hash-function failed verification. PLEASE, always perform such checking at
|
||||
* initialization of your code, if you using MSVC or other troubleful compilers.
|
||||
*/
|
||||
|
||||
T1HA_API int t1ha_selfcheck__all_enabled(void);
|
||||
|
||||
#ifndef T1HA2_DISABLED
|
||||
T1HA_API int t1ha_selfcheck__t1ha2_atonce(void);
|
||||
T1HA_API int t1ha_selfcheck__t1ha2_atonce128(void);
|
||||
T1HA_API int t1ha_selfcheck__t1ha2_stream(void);
|
||||
T1HA_API int t1ha_selfcheck__t1ha2(void);
|
||||
#endif /* T1HA2_DISABLED */
|
||||
|
||||
#ifndef T1HA1_DISABLED
|
||||
T1HA_API int t1ha_selfcheck__t1ha1_le(void);
|
||||
T1HA_API int t1ha_selfcheck__t1ha1_be(void);
|
||||
T1HA_API int t1ha_selfcheck__t1ha1(void);
|
||||
#endif /* T1HA1_DISABLED */
|
||||
|
||||
#ifndef T1HA0_DISABLED
|
||||
T1HA_API int t1ha_selfcheck__t1ha0_32le(void);
|
||||
T1HA_API int t1ha_selfcheck__t1ha0_32be(void);
|
||||
T1HA_API int t1ha_selfcheck__t1ha0(void);
|
||||
|
||||
/* Define T1HA0_AESNI_AVAILABLE to 0 for disable AES-NI support. */
|
||||
#ifndef T1HA0_AESNI_AVAILABLE
|
||||
#if defined(__e2k__) || \
|
||||
(defined(__ia32__) && (!defined(_M_IX86) || _MSC_VER > 1800))
|
||||
#define T1HA0_AESNI_AVAILABLE 1
|
||||
#else
|
||||
#define T1HA0_AESNI_AVAILABLE 0
|
||||
#endif
|
||||
#endif /* ifndef T1HA0_AESNI_AVAILABLE */
|
||||
|
||||
#if T1HA0_AESNI_AVAILABLE
|
||||
T1HA_API int t1ha_selfcheck__t1ha0_ia32aes_noavx(void);
|
||||
T1HA_API int t1ha_selfcheck__t1ha0_ia32aes_avx(void);
|
||||
#ifndef __e2k__
|
||||
T1HA_API int t1ha_selfcheck__t1ha0_ia32aes_avx2(void);
|
||||
#endif
|
||||
#endif /* if T1HA0_AESNI_AVAILABLE */
|
||||
#endif /* T1HA0_DISABLED */
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* t1ha2 = 64 and 128-bit, SLIGHTLY MORE ATTENTION FOR QUALITY AND STRENGTH.
|
||||
*
|
||||
* - The recommended version of "Fast Positive Hash" with good quality
|
||||
* for checksum, hash tables and fingerprinting.
|
||||
* - Portable and extremely efficiency on modern 64-bit CPUs.
|
||||
* Designed for 64-bit little-endian platforms,
|
||||
* in other cases will runs slowly.
|
||||
* - Great quality of hashing and still faster than other non-t1ha hashes.
|
||||
* Provides streaming mode and 128-bit result.
|
||||
*
|
||||
* Note: Due performance reason 64- and 128-bit results are completely
|
||||
* different each other, i.e. 64-bit result is NOT any part of 128-bit.
|
||||
*/
|
||||
#ifndef T1HA2_DISABLED
|
||||
|
||||
/* The at-once variant with 64-bit result */
|
||||
T1HA_API uint64_t t1ha2_atonce(const void *data, size_t length, uint64_t seed);
|
||||
|
||||
/* The at-once variant with 128-bit result.
|
||||
* Argument `extra_result` is NOT optional and MUST be valid.
|
||||
* The high 64-bit part of 128-bit hash will be always unconditionally
|
||||
* stored to the address given by `extra_result` argument. */
|
||||
T1HA_API uint64_t t1ha2_atonce128(uint64_t *__restrict extra_result,
|
||||
const void *__restrict data, size_t length,
|
||||
uint64_t seed);
|
||||
|
||||
/* The init/update/final trinity for streaming.
|
||||
* Return 64 or 128-bit result depentently from `extra_result` argument. */
|
||||
T1HA_API void t1ha2_init(t1ha_context_t *ctx, uint64_t seed_x, uint64_t seed_y);
|
||||
T1HA_API void t1ha2_update(t1ha_context_t *__restrict ctx,
|
||||
const void *__restrict data, size_t length);
|
||||
|
||||
/* Argument `extra_result` is optional and MAY be NULL.
|
||||
* - If `extra_result` is NOT NULL then the 128-bit hash will be calculated,
|
||||
* and high 64-bit part of it will be stored to the address given
|
||||
* by `extra_result` argument.
|
||||
* - Otherwise the 64-bit hash will be calculated
|
||||
* and returned from function directly.
|
||||
*
|
||||
* Note: Due performance reason 64- and 128-bit results are completely
|
||||
* different each other, i.e. 64-bit result is NOT any part of 128-bit. */
|
||||
T1HA_API uint64_t t1ha2_final(t1ha_context_t *__restrict ctx,
|
||||
uint64_t *__restrict extra_result /* optional */);
|
||||
|
||||
#endif /* T1HA2_DISABLED */
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* t1ha1 = 64-bit, BASELINE FAST PORTABLE HASH:
|
||||
*
|
||||
* - Runs faster on 64-bit platforms in other cases may runs slowly.
|
||||
* - Portable and stable, returns same 64-bit result
|
||||
* on all architectures and CPUs.
|
||||
* - Unfortunately it fails the "strict avalanche criteria",
|
||||
* see test results at https://github.com/demerphq/smhasher.
|
||||
*
|
||||
* This flaw is insignificant for the t1ha1() purposes and imperceptible
|
||||
* from a practical point of view.
|
||||
* However, nowadays this issue has resolved in the next t1ha2(),
|
||||
* that was initially planned to providing a bit more quality.
|
||||
*/
|
||||
#ifndef T1HA1_DISABLED
|
||||
|
||||
/* The little-endian variant. */
|
||||
T1HA_API uint64_t t1ha1_le(const void *data, size_t length, uint64_t seed);
|
||||
|
||||
/* The big-endian variant. */
|
||||
T1HA_API uint64_t t1ha1_be(const void *data, size_t length, uint64_t seed);
|
||||
|
||||
#endif /* T1HA1_DISABLED */
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* t1ha0 = 64-bit, JUST ONLY FASTER:
|
||||
*
|
||||
* - Provides fast-as-possible hashing for current CPU, including
|
||||
* 32-bit systems and engaging the available hardware acceleration.
|
||||
* - It is a facade that selects most quick-and-dirty hash
|
||||
* for the current processor. For instance, on IA32 (x86) actual function
|
||||
* will be selected in runtime, depending on current CPU capabilities
|
||||
*
|
||||
* BE CAREFUL!!! THIS IS MEANS:
|
||||
*
|
||||
* 1. The quality of hash is a subject for tradeoffs with performance.
|
||||
* So, the quality and strength of t1ha0() may be lower than t1ha1(),
|
||||
* especially on 32-bit targets, but then much faster.
|
||||
* However, guaranteed that it passes all SMHasher tests.
|
||||
*
|
||||
* 2. No warranty that the hash result will be same for particular
|
||||
* key on another machine or another version of libt1ha.
|
||||
*
|
||||
* Briefly, such hash-results and their derivatives, should be
|
||||
* used only in runtime, but should not be persist or transferred
|
||||
* over a network.
|
||||
*
|
||||
*
|
||||
* When T1HA0_RUNTIME_SELECT is nonzero the t1ha0_resolve() function could
|
||||
* be used to get actual t1ha0() implementation address at runtime. This is
|
||||
* useful for two cases:
|
||||
* - calling by local pointer-to-function usually is little
|
||||
* bit faster (less overhead) than via a PLT thru the DSO boundary.
|
||||
* - GNU Indirect functions (see below) don't supported by environment
|
||||
* and calling by t1ha0_funcptr is not available and/or expensive.
|
||||
*/
|
||||
|
||||
#ifndef T1HA0_DISABLED
|
||||
|
||||
/* The little-endian variant for 32-bit CPU. */
|
||||
uint64_t t1ha0_32le(const void *data, size_t length, uint64_t seed);
|
||||
/* The big-endian variant for 32-bit CPU. */
|
||||
uint64_t t1ha0_32be(const void *data, size_t length, uint64_t seed);
|
||||
|
||||
/* Define T1HA0_AESNI_AVAILABLE to 0 for disable AES-NI support. */
|
||||
#ifndef T1HA0_AESNI_AVAILABLE
|
||||
#if defined(__e2k__) || \
|
||||
(defined(__ia32__) && (!defined(_M_IX86) || _MSC_VER > 1800))
|
||||
#define T1HA0_AESNI_AVAILABLE 1
|
||||
#else
|
||||
#define T1HA0_AESNI_AVAILABLE 0
|
||||
#endif
|
||||
#endif /* T1HA0_AESNI_AVAILABLE */
|
||||
|
||||
/* Define T1HA0_RUNTIME_SELECT to 0 for disable dispatching t1ha0 at runtime. */
|
||||
#ifndef T1HA0_RUNTIME_SELECT
|
||||
#if T1HA0_AESNI_AVAILABLE && !defined(__e2k__)
|
||||
#define T1HA0_RUNTIME_SELECT 1
|
||||
#else
|
||||
#define T1HA0_RUNTIME_SELECT 0
|
||||
#endif
|
||||
#endif /* T1HA0_RUNTIME_SELECT */
|
||||
|
||||
#if !T1HA0_RUNTIME_SELECT && !defined(T1HA0_USE_DEFINE)
|
||||
#if defined(__LCC__)
|
||||
#define T1HA0_USE_DEFINE 1
|
||||
#else
|
||||
#define T1HA0_USE_DEFINE 0
|
||||
#endif
|
||||
#endif /* T1HA0_USE_DEFINE */
|
||||
|
||||
#if T1HA0_AESNI_AVAILABLE
|
||||
uint64_t t1ha0_ia32aes_noavx(const void *data, size_t length, uint64_t seed);
|
||||
uint64_t t1ha0_ia32aes_avx(const void *data, size_t length, uint64_t seed);
|
||||
#ifndef __e2k__
|
||||
uint64_t t1ha0_ia32aes_avx2(const void *data, size_t length, uint64_t seed);
|
||||
#endif
|
||||
#endif /* T1HA0_AESNI_AVAILABLE */
|
||||
|
||||
#if T1HA0_RUNTIME_SELECT
|
||||
typedef uint64_t (*t1ha0_function_t)(const void *, size_t, uint64_t);
|
||||
T1HA_API t1ha0_function_t t1ha0_resolve(void);
|
||||
#if T1HA_USE_INDIRECT_FUNCTIONS
|
||||
T1HA_API uint64_t t1ha0(const void *data, size_t length, uint64_t seed);
|
||||
#else
|
||||
/* Otherwise function pointer will be used.
|
||||
* Unfortunately this may cause some overhead calling. */
|
||||
T1HA_API extern uint64_t (*t1ha0_funcptr)(const void *data, size_t length,
|
||||
uint64_t seed);
|
||||
static __force_inline uint64_t t1ha0(const void *data, size_t length,
|
||||
uint64_t seed) {
|
||||
return t1ha0_funcptr(data, length, seed);
|
||||
}
|
||||
#endif /* T1HA_USE_INDIRECT_FUNCTIONS */
|
||||
|
||||
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
|
||||
#if T1HA0_USE_DEFINE
|
||||
|
||||
#if (UINTPTR_MAX > 0xffffFFFFul || ULONG_MAX > 0xffffFFFFul) && \
|
||||
(!defined(T1HA1_DISABLED) || !defined(T1HA2_DISABLED))
|
||||
#if defined(T1HA1_DISABLED)
|
||||
#define t1ha0 t1ha2_atonce
|
||||
#else
|
||||
#define t1ha0 t1ha1_be
|
||||
#endif /* T1HA1_DISABLED */
|
||||
#else /* 32/64 */
|
||||
#define t1ha0 t1ha0_32be
|
||||
#endif /* 32/64 */
|
||||
|
||||
#else /* T1HA0_USE_DEFINE */
|
||||
|
||||
static __force_inline uint64_t t1ha0(const void *data, size_t length,
|
||||
uint64_t seed) {
|
||||
#if (UINTPTR_MAX > 0xffffFFFFul || ULONG_MAX > 0xffffFFFFul) && \
|
||||
(!defined(T1HA1_DISABLED) || !defined(T1HA2_DISABLED))
|
||||
#if defined(T1HA1_DISABLED)
|
||||
return t1ha2_atonce(data, length, seed);
|
||||
#else
|
||||
return t1ha1_be(data, length, seed);
|
||||
#endif /* T1HA1_DISABLED */
|
||||
#else /* 32/64 */
|
||||
return t1ha0_32be(data, length, seed);
|
||||
#endif /* 32/64 */
|
||||
}
|
||||
|
||||
#endif /* !T1HA0_USE_DEFINE */
|
||||
|
||||
#else /* !T1HA0_RUNTIME_SELECT && __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__ */
|
||||
|
||||
#if T1HA0_USE_DEFINE
|
||||
|
||||
#if (UINTPTR_MAX > 0xffffFFFFul || ULONG_MAX > 0xffffFFFFul) && \
|
||||
(!defined(T1HA1_DISABLED) || !defined(T1HA2_DISABLED))
|
||||
#if defined(T1HA1_DISABLED)
|
||||
#define t1ha0 t1ha2_atonce
|
||||
#else
|
||||
#define t1ha0 t1ha1_le
|
||||
#endif /* T1HA1_DISABLED */
|
||||
#else /* 32/64 */
|
||||
#define t1ha0 t1ha0_32le
|
||||
#endif /* 32/64 */
|
||||
|
||||
#else
|
||||
|
||||
static __force_inline uint64_t t1ha0(const void *data, size_t length,
|
||||
uint64_t seed) {
|
||||
#if (UINTPTR_MAX > 0xffffFFFFul || ULONG_MAX > 0xffffFFFFul) && \
|
||||
(!defined(T1HA1_DISABLED) || !defined(T1HA2_DISABLED))
|
||||
#if defined(T1HA1_DISABLED)
|
||||
return t1ha2_atonce(data, length, seed);
|
||||
#else
|
||||
return t1ha1_le(data, length, seed);
|
||||
#endif /* T1HA1_DISABLED */
|
||||
#else /* 32/64 */
|
||||
return t1ha0_32le(data, length, seed);
|
||||
#endif /* 32/64 */
|
||||
}
|
||||
|
||||
#endif /* !T1HA0_USE_DEFINE */
|
||||
|
||||
#endif /* !T1HA0_RUNTIME_SELECT */
|
||||
|
||||
#endif /* T1HA0_DISABLED */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#if __GNUC_PREREQ(4, 0)
|
||||
#pragma GCC visibility pop
|
||||
#endif /* __GNUC_PREREQ(4,0) */
|
||||
@@ -1,329 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2018 Positive Technologies, https://www.ptsecurity.com,
|
||||
* Fast Positive Hash.
|
||||
*
|
||||
* Portions Copyright (c) 2010-2018 Leonid Yuriev <leo@yuriev.ru>,
|
||||
* The 1Hippeus project (t1h).
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgement in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
* t1ha = { Fast Positive Hash, aka "Позитивный Хэш" }
|
||||
* by [Positive Technologies](https://www.ptsecurity.ru)
|
||||
*
|
||||
* Briefly, it is a 64-bit Hash Function:
|
||||
* 1. Created for 64-bit little-endian platforms, in predominantly for x86_64,
|
||||
* but portable and without penalties it can run on any 64-bit CPU.
|
||||
* 2. In most cases up to 15% faster than City64, xxHash, mum-hash, metro-hash
|
||||
* and all others portable hash-functions (which do not use specific
|
||||
* hardware tricks).
|
||||
* 3. Not suitable for cryptography.
|
||||
*
|
||||
* The Future will Positive. Всё будет хорошо.
|
||||
*
|
||||
* ACKNOWLEDGEMENT:
|
||||
* The t1ha was originally developed by Leonid Yuriev (Леонид Юрьев)
|
||||
* for The 1Hippeus project - zerocopy messaging in the spirit of Sparta!
|
||||
*/
|
||||
|
||||
#ifndef T1HA2_DISABLED
|
||||
#include "t1ha_bits.h"
|
||||
//#include "t1ha_selfcheck.h"
|
||||
|
||||
static __always_inline void init_ab(t1ha_state256_t *s, uint64_t x,
|
||||
uint64_t y) {
|
||||
s->n.a = x;
|
||||
s->n.b = y;
|
||||
}
|
||||
|
||||
static __always_inline void init_cd(t1ha_state256_t *s, uint64_t x,
|
||||
uint64_t y) {
|
||||
s->n.c = rot64(y, 23) + ~x;
|
||||
s->n.d = ~y + rot64(x, 19);
|
||||
}
|
||||
|
||||
/* TODO: C++ template in the next version */
|
||||
#define T1HA2_UPDATE(ENDIANNES, ALIGNESS, state, v) \
|
||||
do { \
|
||||
t1ha_state256_t *const s = state; \
|
||||
const uint64_t w0 = fetch64_##ENDIANNES##_##ALIGNESS(v + 0); \
|
||||
const uint64_t w1 = fetch64_##ENDIANNES##_##ALIGNESS(v + 1); \
|
||||
const uint64_t w2 = fetch64_##ENDIANNES##_##ALIGNESS(v + 2); \
|
||||
const uint64_t w3 = fetch64_##ENDIANNES##_##ALIGNESS(v + 3); \
|
||||
\
|
||||
const uint64_t d02 = w0 + rot64(w2 + s->n.d, 56); \
|
||||
const uint64_t c13 = w1 + rot64(w3 + s->n.c, 19); \
|
||||
s->n.d ^= s->n.b + rot64(w1, 38); \
|
||||
s->n.c ^= s->n.a + rot64(w0, 57); \
|
||||
s->n.b ^= prime_6 * (c13 + w2); \
|
||||
s->n.a ^= prime_5 * (d02 + w3); \
|
||||
} while (0)
|
||||
|
||||
static __always_inline void squash(t1ha_state256_t *s) {
|
||||
s->n.a ^= prime_6 * (s->n.c + rot64(s->n.d, 23));
|
||||
s->n.b ^= prime_5 * (rot64(s->n.c, 19) + s->n.d);
|
||||
}
|
||||
|
||||
/* TODO: C++ template in the next version */
|
||||
#define T1HA2_LOOP(ENDIANNES, ALIGNESS, state, data, len) \
|
||||
do { \
|
||||
const void *detent = (const uint8_t *)data + len - 31; \
|
||||
do { \
|
||||
const uint64_t *v = (const uint64_t *)data; \
|
||||
data = (const uint64_t *)data + 4; \
|
||||
prefetch(data); \
|
||||
T1HA2_UPDATE(le, ALIGNESS, state, v); \
|
||||
} while (likely(data < detent)); \
|
||||
} while (0)
|
||||
|
||||
/* TODO: C++ template in the next version */
|
||||
#define T1HA2_TAIL_AB(ENDIANNES, ALIGNESS, state, data, len) \
|
||||
do { \
|
||||
t1ha_state256_t *const s = state; \
|
||||
const uint64_t *v = (const uint64_t *)data; \
|
||||
switch (len) { \
|
||||
default: \
|
||||
mixup64(&s->n.a, &s->n.b, fetch64_##ENDIANNES##_##ALIGNESS(v++), \
|
||||
prime_4); \
|
||||
/* fall through */ \
|
||||
case 24: \
|
||||
case 23: \
|
||||
case 22: \
|
||||
case 21: \
|
||||
case 20: \
|
||||
case 19: \
|
||||
case 18: \
|
||||
case 17: \
|
||||
mixup64(&s->n.b, &s->n.a, fetch64_##ENDIANNES##_##ALIGNESS(v++), \
|
||||
prime_3); \
|
||||
/* fall through */ \
|
||||
case 16: \
|
||||
case 15: \
|
||||
case 14: \
|
||||
case 13: \
|
||||
case 12: \
|
||||
case 11: \
|
||||
case 10: \
|
||||
case 9: \
|
||||
mixup64(&s->n.a, &s->n.b, fetch64_##ENDIANNES##_##ALIGNESS(v++), \
|
||||
prime_2); \
|
||||
/* fall through */ \
|
||||
case 8: \
|
||||
case 7: \
|
||||
case 6: \
|
||||
case 5: \
|
||||
case 4: \
|
||||
case 3: \
|
||||
case 2: \
|
||||
case 1: \
|
||||
mixup64(&s->n.b, &s->n.a, tail64_##ENDIANNES##_##ALIGNESS(v, len), \
|
||||
prime_1); \
|
||||
/* fall through */ \
|
||||
case 0: \
|
||||
return final64(s->n.a, s->n.b); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* TODO: C++ template in the next version */
|
||||
#define T1HA2_TAIL_ABCD(ENDIANNES, ALIGNESS, state, data, len) \
|
||||
do { \
|
||||
t1ha_state256_t *const s = state; \
|
||||
const uint64_t *v = (const uint64_t *)data; \
|
||||
switch (len) { \
|
||||
default: \
|
||||
mixup64(&s->n.a, &s->n.d, fetch64_##ENDIANNES##_##ALIGNESS(v++), \
|
||||
prime_4); \
|
||||
/* fall through */ \
|
||||
case 24: \
|
||||
case 23: \
|
||||
case 22: \
|
||||
case 21: \
|
||||
case 20: \
|
||||
case 19: \
|
||||
case 18: \
|
||||
case 17: \
|
||||
mixup64(&s->n.b, &s->n.a, fetch64_##ENDIANNES##_##ALIGNESS(v++), \
|
||||
prime_3); \
|
||||
/* fall through */ \
|
||||
case 16: \
|
||||
case 15: \
|
||||
case 14: \
|
||||
case 13: \
|
||||
case 12: \
|
||||
case 11: \
|
||||
case 10: \
|
||||
case 9: \
|
||||
mixup64(&s->n.c, &s->n.b, fetch64_##ENDIANNES##_##ALIGNESS(v++), \
|
||||
prime_2); \
|
||||
/* fall through */ \
|
||||
case 8: \
|
||||
case 7: \
|
||||
case 6: \
|
||||
case 5: \
|
||||
case 4: \
|
||||
case 3: \
|
||||
case 2: \
|
||||
case 1: \
|
||||
mixup64(&s->n.d, &s->n.c, tail64_##ENDIANNES##_##ALIGNESS(v, len), \
|
||||
prime_1); \
|
||||
/* fall through */ \
|
||||
case 0: \
|
||||
return final128(s->n.a, s->n.b, s->n.c, s->n.d, extra_result); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static __always_inline uint64_t final128(uint64_t a, uint64_t b, uint64_t c,
|
||||
uint64_t d, uint64_t *h) {
|
||||
mixup64(&a, &b, rot64(c, 41) ^ d, prime_0);
|
||||
mixup64(&b, &c, rot64(d, 23) ^ a, prime_6);
|
||||
mixup64(&c, &d, rot64(a, 19) ^ b, prime_5);
|
||||
mixup64(&d, &a, rot64(b, 31) ^ c, prime_4);
|
||||
*h = c + d;
|
||||
return a ^ b;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
uint64_t t1ha2_atonce(const void *data, size_t length, uint64_t seed) {
|
||||
t1ha_state256_t state;
|
||||
init_ab(&state, seed, length);
|
||||
|
||||
#if T1HA_SYS_UNALIGNED_ACCESS == T1HA_UNALIGNED_ACCESS__EFFICIENT
|
||||
if (unlikely(length > 32)) {
|
||||
init_cd(&state, seed, length);
|
||||
T1HA2_LOOP(le, unaligned, &state, data, length);
|
||||
squash(&state);
|
||||
length &= 31;
|
||||
}
|
||||
T1HA2_TAIL_AB(le, unaligned, &state, data, length);
|
||||
#else
|
||||
const bool misaligned = (((uintptr_t)data) & (ALIGNMENT_64 - 1)) != 0;
|
||||
if (misaligned) {
|
||||
if (unlikely(length > 32)) {
|
||||
init_cd(&state, seed, length);
|
||||
T1HA2_LOOP(le, unaligned, &state, data, length);
|
||||
squash(&state);
|
||||
length &= 31;
|
||||
}
|
||||
T1HA2_TAIL_AB(le, unaligned, &state, data, length);
|
||||
} else {
|
||||
if (unlikely(length > 32)) {
|
||||
init_cd(&state, seed, length);
|
||||
T1HA2_LOOP(le, aligned, &state, data, length);
|
||||
squash(&state);
|
||||
length &= 31;
|
||||
}
|
||||
T1HA2_TAIL_AB(le, aligned, &state, data, length);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64_t t1ha2_atonce128(uint64_t *__restrict extra_result,
|
||||
const void *__restrict data, size_t length,
|
||||
uint64_t seed) {
|
||||
t1ha_state256_t state;
|
||||
init_ab(&state, seed, length);
|
||||
init_cd(&state, seed, length);
|
||||
|
||||
#if T1HA_SYS_UNALIGNED_ACCESS == T1HA_UNALIGNED_ACCESS__EFFICIENT
|
||||
if (unlikely(length > 32)) {
|
||||
T1HA2_LOOP(le, unaligned, &state, data, length);
|
||||
length &= 31;
|
||||
}
|
||||
T1HA2_TAIL_ABCD(le, unaligned, &state, data, length);
|
||||
#else
|
||||
const bool misaligned = (((uintptr_t)data) & (ALIGNMENT_64 - 1)) != 0;
|
||||
if (misaligned) {
|
||||
if (unlikely(length > 32)) {
|
||||
T1HA2_LOOP(le, unaligned, &state, data, length);
|
||||
length &= 31;
|
||||
}
|
||||
T1HA2_TAIL_ABCD(le, unaligned, &state, data, length);
|
||||
} else {
|
||||
if (unlikely(length > 32)) {
|
||||
T1HA2_LOOP(le, aligned, &state, data, length);
|
||||
length &= 31;
|
||||
}
|
||||
T1HA2_TAIL_ABCD(le, aligned, &state, data, length);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void t1ha2_init(t1ha_context_t *ctx, uint64_t seed_x, uint64_t seed_y) {
|
||||
init_ab(&ctx->state, seed_x, seed_y);
|
||||
init_cd(&ctx->state, seed_x, seed_y);
|
||||
ctx->partial = 0;
|
||||
ctx->total = 0;
|
||||
}
|
||||
|
||||
void t1ha2_update(t1ha_context_t *__restrict ctx, const void *__restrict data,
|
||||
size_t length) {
|
||||
ctx->total += length;
|
||||
|
||||
if (ctx->partial) {
|
||||
const size_t left = 32 - ctx->partial;
|
||||
const size_t chunk = (length >= left) ? left : length;
|
||||
memcpy(ctx->buffer.bytes + ctx->partial, data, chunk);
|
||||
ctx->partial += chunk;
|
||||
if (ctx->partial < 32) {
|
||||
assert(left >= length);
|
||||
return;
|
||||
}
|
||||
ctx->partial = 0;
|
||||
data = (const uint8_t *)data + chunk;
|
||||
length -= chunk;
|
||||
T1HA2_UPDATE(le, aligned, &ctx->state, ctx->buffer.u64);
|
||||
}
|
||||
|
||||
if (length >= 32) {
|
||||
#if T1HA_SYS_UNALIGNED_ACCESS == T1HA_UNALIGNED_ACCESS__EFFICIENT
|
||||
T1HA2_LOOP(le, unaligned, &ctx->state, data, length);
|
||||
#else
|
||||
const bool misaligned = (((uintptr_t)data) & (ALIGNMENT_64 - 1)) != 0;
|
||||
if (misaligned) {
|
||||
T1HA2_LOOP(le, unaligned, &ctx->state, data, length);
|
||||
} else {
|
||||
T1HA2_LOOP(le, aligned, &ctx->state, data, length);
|
||||
}
|
||||
#endif
|
||||
length &= 31;
|
||||
}
|
||||
|
||||
if (length)
|
||||
memcpy(ctx->buffer.bytes, data, ctx->partial = length);
|
||||
}
|
||||
|
||||
uint64_t t1ha2_final(t1ha_context_t *__restrict ctx,
|
||||
uint64_t *__restrict extra_result) {
|
||||
uint64_t bits = (ctx->total << 3) ^ (UINT64_C(1) << 63);
|
||||
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
|
||||
bits = bswap64(bits);
|
||||
#endif
|
||||
t1ha2_update(ctx, &bits, 8);
|
||||
|
||||
if (likely(!extra_result)) {
|
||||
squash(&ctx->state);
|
||||
T1HA2_TAIL_AB(le, aligned, &ctx->state, ctx->buffer.u64, ctx->partial);
|
||||
}
|
||||
|
||||
T1HA2_TAIL_ABCD(le, aligned, &ctx->state, ctx->buffer.u64, ctx->partial);
|
||||
}
|
||||
|
||||
#endif /* T1HA2_DISABLED */
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user