mirror of
https://github.com/wownero/RandomX.git
synced 2026-10-19 08:18:45 +02:00
Replaced division instructions with IMUL_RCP
This commit is contained in:
@@ -17,12 +17,10 @@ You should have received a copy of the GNU General Public License
|
||||
along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
//#define TRACE
|
||||
#define MAGIC_DIVISION
|
||||
|
||||
#include "AssemblyGeneratorX86.hpp"
|
||||
#include "common.hpp"
|
||||
#ifdef MAGIC_DIVISION
|
||||
#include "divideByConstantCodegen.h"
|
||||
#endif
|
||||
#include "reciprocal.h"
|
||||
#include "Program.hpp"
|
||||
|
||||
namespace RandomX {
|
||||
@@ -276,38 +274,12 @@ namespace RandomX {
|
||||
traceint(instr);
|
||||
}
|
||||
|
||||
//~6 uOPs
|
||||
void AssemblyGeneratorX86::h_IDIV_C(Instruction& instr, int i) {
|
||||
//2 uOPs
|
||||
void AssemblyGeneratorX86::h_IMUL_RCP(Instruction& instr, int i) {
|
||||
if (instr.imm32 != 0) {
|
||||
uint32_t divisor = instr.imm32;
|
||||
if (divisor & (divisor - 1)) {
|
||||
magicu_info mi = compute_unsigned_magic_info(divisor, sizeof(uint64_t) * 8);
|
||||
if (mi.pre_shift == 0 && !mi.increment) {
|
||||
asmCode << "\tmov rax, " << mi.multiplier << std::endl;
|
||||
asmCode << "\tmul " << regR[instr.dst] << std::endl;
|
||||
}
|
||||
else {
|
||||
asmCode << "\tmov rax, " << regR[instr.dst] << std::endl;
|
||||
if (mi.pre_shift > 0)
|
||||
asmCode << "\tshr rax, " << mi.pre_shift << std::endl;
|
||||
if (mi.increment) {
|
||||
asmCode << "\tadd rax, 1" << std::endl;
|
||||
asmCode << "\tsbb rax, 0" << std::endl;
|
||||
}
|
||||
asmCode << "\tmov rcx, " << mi.multiplier << std::endl;
|
||||
asmCode << "\tmul rcx" << std::endl;
|
||||
}
|
||||
if (mi.post_shift > 0)
|
||||
asmCode << "\tshr rdx, " << mi.post_shift << std::endl;
|
||||
asmCode << "\tadd " << regR[instr.dst] << ", rdx" << std::endl;
|
||||
}
|
||||
else { //divisor is a power of two
|
||||
int shift = 0;
|
||||
while (divisor >>= 1)
|
||||
++shift;
|
||||
if(shift > 0)
|
||||
asmCode << "\tshr " << regR[instr.dst] << ", " << shift << std::endl;
|
||||
}
|
||||
asmCode << "\tmov rax, " << reciprocal(instr.imm32) << std::endl;
|
||||
asmCode << "\timul " << regR[instr.dst] << ", rax" << std::endl;
|
||||
traceint(instr);
|
||||
}
|
||||
else {
|
||||
@@ -317,59 +289,7 @@ namespace RandomX {
|
||||
|
||||
//~8.5 uOPs
|
||||
void AssemblyGeneratorX86::h_ISDIV_C(Instruction& instr, int i) {
|
||||
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
|
||||
bool negative = divisor < 0;
|
||||
if (negative)
|
||||
divisor = -divisor;
|
||||
int shift = 0;
|
||||
uint64_t unsignedDivisor = divisor;
|
||||
while (unsignedDivisor >>= 1)
|
||||
++shift;
|
||||
if (shift > 0) {
|
||||
asmCode << "\tmov rcx, rax" << std::endl;
|
||||
asmCode << "\tsar rcx, 63" << std::endl;
|
||||
uint32_t mask = (1ULL << shift) + 0xFFFFFFFF;
|
||||
asmCode << "\tand ecx, 0" << std::hex << mask << std::dec << "h" << std::endl;
|
||||
asmCode << "\tadd rax, rcx" << std::endl;
|
||||
asmCode << "\tsar rax, " << shift << std::endl;
|
||||
}
|
||||
if (negative)
|
||||
asmCode << "\tneg rax" << std::endl;
|
||||
asmCode << "\tadd " << regR[instr.dst] << ", rax" << std::endl;
|
||||
traceint(instr);
|
||||
}
|
||||
else if (divisor != 0) {
|
||||
magics_info mi = compute_signed_magic_info(divisor);
|
||||
asmCode << "\tmov rax, " << mi.multiplier << std::endl;
|
||||
asmCode << "\timul " << regR[instr.dst] << std::endl;
|
||||
//asmCode << "\tmov rax, rdx" << std::endl;
|
||||
asmCode << "\txor eax, eax" << std::endl;
|
||||
bool haveSF = false;
|
||||
if (divisor > 0 && mi.multiplier < 0) {
|
||||
asmCode << "\tadd rdx, " << regR[instr.dst] << std::endl;
|
||||
haveSF = true;
|
||||
}
|
||||
if (divisor < 0 && mi.multiplier > 0) {
|
||||
asmCode << "\tsub rdx, " << regR[instr.dst] << std::endl;
|
||||
haveSF = true;
|
||||
}
|
||||
if (mi.shift > 0) {
|
||||
asmCode << "\tsar rdx, " << mi.shift << std::endl;
|
||||
haveSF = true;
|
||||
}
|
||||
if (!haveSF)
|
||||
asmCode << "\ttest rdx, rdx" << std::endl;
|
||||
asmCode << "\tsets al" << std::endl;
|
||||
asmCode << "\tadd rdx, rax" << std::endl;
|
||||
asmCode << "\tadd " << regR[instr.dst] << ", rdx" << std::endl;
|
||||
traceint(instr);
|
||||
}
|
||||
else {
|
||||
tracenop(instr);
|
||||
}
|
||||
tracenop(instr);
|
||||
}
|
||||
|
||||
//2 uOPs
|
||||
@@ -570,7 +490,7 @@ namespace RandomX {
|
||||
INST_HANDLE(IMULH_M)
|
||||
INST_HANDLE(ISMULH_R)
|
||||
INST_HANDLE(ISMULH_M)
|
||||
INST_HANDLE(IDIV_C)
|
||||
INST_HANDLE(IMUL_RCP)
|
||||
INST_HANDLE(ISDIV_C)
|
||||
INST_HANDLE(INEG_R)
|
||||
INST_HANDLE(IXOR_R)
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace RandomX {
|
||||
void h_IMULH_M(Instruction&, int);
|
||||
void h_ISMULH_R(Instruction&, int);
|
||||
void h_ISMULH_M(Instruction&, int);
|
||||
void h_IDIV_C(Instruction&, int);
|
||||
void h_IMUL_RCP(Instruction&, int);
|
||||
void h_ISDIV_C(Instruction&, int);
|
||||
void h_INEG_R(Instruction&, int);
|
||||
void h_IXOR_R(Instruction&, int);
|
||||
|
||||
+3
-3
@@ -193,7 +193,7 @@ namespace RandomX {
|
||||
}
|
||||
}
|
||||
|
||||
void Instruction::h_IDIV_C(std::ostream& os) const {
|
||||
void Instruction::h_IMUL_RCP(std::ostream& os) const {
|
||||
os << "r" << (int)dst << ", " << imm32 << std::endl;
|
||||
}
|
||||
|
||||
@@ -345,7 +345,7 @@ namespace RandomX {
|
||||
INST_NAME(IMULH_M)
|
||||
INST_NAME(ISMULH_R)
|
||||
INST_NAME(ISMULH_M)
|
||||
INST_NAME(IDIV_C)
|
||||
INST_NAME(IMUL_RCP)
|
||||
INST_NAME(ISDIV_C)
|
||||
INST_NAME(INEG_R)
|
||||
INST_NAME(IXOR_R)
|
||||
@@ -396,7 +396,7 @@ namespace RandomX {
|
||||
INST_HANDLE(IMULH_M)
|
||||
INST_HANDLE(ISMULH_R)
|
||||
INST_HANDLE(ISMULH_M)
|
||||
INST_HANDLE(IDIV_C)
|
||||
INST_HANDLE(IMUL_RCP)
|
||||
INST_HANDLE(ISDIV_C)
|
||||
INST_HANDLE(INEG_R)
|
||||
INST_HANDLE(IXOR_R)
|
||||
|
||||
+3
-3
@@ -41,8 +41,8 @@ namespace RandomX {
|
||||
constexpr int IMULH_M = 9;
|
||||
constexpr int ISMULH_R = 10;
|
||||
constexpr int ISMULH_M = 11;
|
||||
constexpr int IDIV_C = 12;
|
||||
constexpr int ISDIV_C = 13;
|
||||
constexpr int IMUL_RCP = 12;
|
||||
//constexpr int ISDIV_C = 13;
|
||||
constexpr int INEG_R = 14;
|
||||
constexpr int IXOR_R = 15;
|
||||
constexpr int IXOR_M = 16;
|
||||
@@ -103,7 +103,7 @@ namespace RandomX {
|
||||
void h_IMULH_M(std::ostream&) const;
|
||||
void h_ISMULH_R(std::ostream&) const;
|
||||
void h_ISMULH_M(std::ostream&) const;
|
||||
void h_IDIV_C(std::ostream&) const;
|
||||
void h_IMUL_RCP(std::ostream&) const;
|
||||
void h_ISDIV_C(std::ostream&) const;
|
||||
void h_INEG_R(std::ostream&) const;
|
||||
void h_IXOR_R(std::ostream&) const;
|
||||
|
||||
@@ -30,6 +30,7 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
#include <cfloat>
|
||||
#include <thread>
|
||||
#include "intrinPortable.h"
|
||||
#include "reciprocal.h"
|
||||
#ifdef STATS
|
||||
#include <algorithm>
|
||||
#endif
|
||||
@@ -136,7 +137,7 @@ namespace RandomX {
|
||||
*ibc.idst += 8 * *ibc.idst + ibc.imm;
|
||||
} break;
|
||||
|
||||
case InstructionType::IMUL_R: {
|
||||
case InstructionType::IMUL_R: { //also handles IMUL_RCP
|
||||
*ibc.idst *= *ibc.isrc;
|
||||
} break;
|
||||
|
||||
@@ -160,24 +161,6 @@ namespace RandomX {
|
||||
*ibc.idst = smulh(unsigned64ToSigned2sCompl(*ibc.idst), unsigned64ToSigned2sCompl(load64(scratchpad + (*ibc.isrc & ibc.memMask))));
|
||||
} break;
|
||||
|
||||
case InstructionType::IDIV_C: {
|
||||
uint64_t dividend = *ibc.idst;
|
||||
uint64_t quotient = dividend / ibc.imm;
|
||||
*ibc.idst += quotient;
|
||||
} break;
|
||||
|
||||
case InstructionType::ISDIV_C: {
|
||||
if (ibc.simm != -1) {
|
||||
int64_t dividend = unsigned64ToSigned2sCompl(*ibc.idst);
|
||||
int64_t quotient = dividend / ibc.simm;
|
||||
*ibc.idst += quotient;
|
||||
}
|
||||
else {
|
||||
uint64_t quotient = ~(*ibc.idst) + 1;
|
||||
*ibc.idst += quotient;
|
||||
}
|
||||
} break;
|
||||
|
||||
case InstructionType::INEG_R: {
|
||||
*ibc.idst = ~(*ibc.idst) + 1; //two's complement negative
|
||||
} break;
|
||||
@@ -568,13 +551,14 @@ namespace RandomX {
|
||||
}
|
||||
} break;
|
||||
|
||||
CASE_REP(IDIV_C) {
|
||||
CASE_REP(IMUL_RCP) {
|
||||
uint32_t divisor = instr.imm32;
|
||||
if (divisor != 0) {
|
||||
auto dst = instr.dst % RegistersCount;
|
||||
ibc.type = InstructionType::IDIV_C;
|
||||
ibc.type = InstructionType::IMUL_R;
|
||||
ibc.idst = &r[dst];
|
||||
ibc.imm = divisor;
|
||||
ibc.imm = reciprocal(divisor);
|
||||
ibc.isrc = &ibc.imm;
|
||||
}
|
||||
else {
|
||||
ibc.type = InstructionType::NOP;
|
||||
@@ -582,16 +566,7 @@ namespace RandomX {
|
||||
} break;
|
||||
|
||||
CASE_REP(ISDIV_C) {
|
||||
int32_t divisor = unsigned32ToSigned2sCompl(instr.imm32);
|
||||
if (divisor != 0) {
|
||||
auto dst = instr.dst % RegistersCount;
|
||||
ibc.type = InstructionType::ISDIV_C;
|
||||
ibc.idst = &r[dst];
|
||||
ibc.simm = divisor;
|
||||
}
|
||||
else {
|
||||
ibc.type = InstructionType::NOP;
|
||||
}
|
||||
ibc.type = InstructionType::NOP;
|
||||
} break;
|
||||
|
||||
CASE_REP(INEG_R) {
|
||||
|
||||
+8
-97
@@ -21,7 +21,7 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
#include <stdexcept>
|
||||
#include "JitCompilerX86.hpp"
|
||||
#include "Program.hpp"
|
||||
#include "divideByConstantCodegen.h"
|
||||
#include "reciprocal.h"
|
||||
#include "virtualMemory.hpp"
|
||||
|
||||
namespace RandomX {
|
||||
@@ -395,106 +395,17 @@ namespace RandomX {
|
||||
emitByte(0xc2 + 8 * instr.dst);
|
||||
}
|
||||
|
||||
void JitCompilerX86::h_IDIV_C(Instruction& instr) {
|
||||
void JitCompilerX86::h_IMUL_RCP(Instruction& instr) {
|
||||
if (instr.imm32 != 0) {
|
||||
uint32_t divisor = instr.imm32;
|
||||
if (divisor & (divisor - 1)) {
|
||||
magicu_info mi = compute_unsigned_magic_info(divisor, sizeof(uint64_t) * 8);
|
||||
if (mi.pre_shift == 0 && !mi.increment) {
|
||||
emit(MOV_RAX_I);
|
||||
emit64(mi.multiplier);
|
||||
emit(REX_MUL_R);
|
||||
emitByte(0xe0 + instr.dst);
|
||||
}
|
||||
else {
|
||||
emit(REX_MOV_RR64);
|
||||
emitByte(0xc0 + instr.dst);
|
||||
if (mi.pre_shift > 0) {
|
||||
emit(REX_SHR_RAX);
|
||||
emitByte(mi.pre_shift);
|
||||
}
|
||||
if (mi.increment) {
|
||||
emit(RAX_ADD_SBB_1);
|
||||
}
|
||||
emit(MOV_RCX_I);
|
||||
emit64(mi.multiplier);
|
||||
emit(MUL_RCX);
|
||||
}
|
||||
if (mi.post_shift > 0) {
|
||||
emit(REX_SHR_RDX);
|
||||
emitByte(mi.post_shift);
|
||||
}
|
||||
emit(REX_ADD_RM);
|
||||
emitByte(0xc2 + 8 * instr.dst);
|
||||
}
|
||||
else { //divisor is a power of two
|
||||
int shift = 0;
|
||||
while (divisor >>= 1)
|
||||
++shift;
|
||||
if (shift > 0) {
|
||||
emit(REX_SH);
|
||||
emitByte(0xe8 + instr.dst);
|
||||
}
|
||||
}
|
||||
emit(MOV_RAX_I);
|
||||
emit64(reciprocal(instr.imm32));
|
||||
emit(REX_IMUL_RM);
|
||||
emitByte(0xc0 + 8 * instr.dst);
|
||||
}
|
||||
}
|
||||
|
||||
void JitCompilerX86::h_ISDIV_C(Instruction& instr) {
|
||||
int64_t divisor = (int32_t)instr.imm32;
|
||||
if ((divisor & -divisor) == divisor || (divisor & -divisor) == -divisor) {
|
||||
emit(REX_MOV_RR64);
|
||||
emitByte(0xc0 + instr.dst);
|
||||
// +/- power of two
|
||||
bool negative = divisor < 0;
|
||||
if (negative)
|
||||
divisor = -divisor;
|
||||
int shift = 0;
|
||||
uint64_t unsignedDivisor = divisor;
|
||||
while (unsignedDivisor >>= 1)
|
||||
++shift;
|
||||
if (shift > 0) {
|
||||
emit(MOV_RCX_RAX_SAR_RCX_63);
|
||||
uint32_t mask = (1ULL << shift) - 1;
|
||||
emit(AND_ECX_I);
|
||||
emit32(mask);
|
||||
emit(ADD_RAX_RCX);
|
||||
emit(SAR_RAX_I8);
|
||||
emitByte(shift);
|
||||
}
|
||||
if (negative)
|
||||
emit(NEG_RAX);
|
||||
emit(ADD_R_RAX);
|
||||
emitByte(0xc0 + instr.dst);
|
||||
}
|
||||
else if (divisor != 0) {
|
||||
magics_info mi = compute_signed_magic_info(divisor);
|
||||
emit(MOV_RAX_I);
|
||||
emit64(mi.multiplier);
|
||||
emit(REX_MUL_R);
|
||||
emitByte(0xe8 + instr.dst);
|
||||
emit(XOR_EAX_EAX);
|
||||
bool haveSF = false;
|
||||
if (divisor > 0 && mi.multiplier < 0) {
|
||||
emit(ADD_RDX_R);
|
||||
emitByte(0xc2 + 8 * instr.dst);
|
||||
haveSF = true;
|
||||
}
|
||||
if (divisor < 0 && mi.multiplier > 0) {
|
||||
emit(SUB_RDX_R);
|
||||
emitByte(0xc2 + 8 * instr.dst);
|
||||
haveSF = true;
|
||||
}
|
||||
if (mi.shift > 0) {
|
||||
emit(SAR_RDX_I8);
|
||||
emitByte(mi.shift);
|
||||
haveSF = true;
|
||||
}
|
||||
if (!haveSF)
|
||||
emit(TEST_RDX_RDX);
|
||||
emit(SETS_AL_ADD_RDX_RAX);
|
||||
emit(ADD_R_RAX);
|
||||
emitByte(0xc2 + 8 * instr.dst);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void JitCompilerX86::h_INEG_R(Instruction& instr) {
|
||||
@@ -748,7 +659,7 @@ namespace RandomX {
|
||||
INST_HANDLE(IMULH_M)
|
||||
INST_HANDLE(ISMULH_R)
|
||||
INST_HANDLE(ISMULH_M)
|
||||
INST_HANDLE(IDIV_C)
|
||||
INST_HANDLE(IMUL_RCP)
|
||||
INST_HANDLE(ISDIV_C)
|
||||
INST_HANDLE(INEG_R)
|
||||
INST_HANDLE(IXOR_R)
|
||||
|
||||
@@ -101,7 +101,7 @@ namespace RandomX {
|
||||
void h_IMULH_M(Instruction&);
|
||||
void h_ISMULH_R(Instruction&);
|
||||
void h_ISMULH_M(Instruction&);
|
||||
void h_IDIV_C(Instruction&);
|
||||
void h_IMUL_RCP(Instruction&);
|
||||
void h_ISDIV_C(Instruction&);
|
||||
void h_INEG_R(Instruction&);
|
||||
void h_IXOR_R(Instruction&);
|
||||
|
||||
@@ -32,8 +32,8 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
#define WT_IMULH_M 1
|
||||
#define WT_ISMULH_R 4
|
||||
#define WT_ISMULH_M 1
|
||||
#define WT_IDIV_C 4
|
||||
#define WT_ISDIV_C 4
|
||||
#define WT_IMUL_RCP 8
|
||||
#define WT_ISDIV_C 0
|
||||
#define WT_INEG_R 2
|
||||
#define WT_IXOR_R 16
|
||||
#define WT_IXOR_M 4
|
||||
@@ -71,7 +71,7 @@ along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
|
||||
constexpr int wtSum = WT_IADD_R + WT_IADD_M + WT_IADD_RC + WT_ISUB_R + \
|
||||
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_IMULH_M + WT_ISMULH_R + WT_ISMULH_M + WT_IMUL_RCP + 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_FSCAL_R + WT_FMUL_R + WT_FMUL_M + WT_FDIV_R + WT_FDIV_M + \
|
||||
|
||||
+1
-1
@@ -341,7 +341,7 @@ int main(int argc, char** argv) {
|
||||
std::cout << "Calculated result: ";
|
||||
result.print(std::cout);
|
||||
if(programCount == 1000)
|
||||
std::cout << "Reference result: fe31e8fd7ed1cec773e87c0684b66b38e58b23ab255e8f9c6b62745e43a26851" << std::endl;
|
||||
std::cout << "Reference result: d3ae5a9365196ed48bb98ebfc3316498e29443ea7f056ecbd272f749c6af7730" << std::endl;
|
||||
if (!miningMode) {
|
||||
std::cout << "Performance: " << 1000 * elapsed / programCount << " ms per hash" << std::endl;
|
||||
}
|
||||
|
||||
+679
-699
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
Copyright (c) 2019 tevador
|
||||
|
||||
This file is part of RandomX.
|
||||
|
||||
RandomX is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RandomX is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "reciprocal.h"
|
||||
|
||||
|
||||
/*
|
||||
Calculates rcp = 2**x / divisor for highest integer x such that rcp < 2**64.
|
||||
|
||||
Equivalent x86 assembly (divisor in rcx):
|
||||
|
||||
mov edx, 1
|
||||
mov r8, rcx
|
||||
xor eax, eax
|
||||
bsr rcx, rcx
|
||||
shl rdx, cl
|
||||
div r8
|
||||
ret
|
||||
|
||||
*/
|
||||
uint64_t reciprocal(uint64_t divisor) {
|
||||
|
||||
const uint64_t p2exp63 = 1ULL << 63;
|
||||
|
||||
uint64_t quotient = p2exp63 / divisor, remainder = p2exp63 % divisor;
|
||||
|
||||
unsigned bsr = 0; //highest set bit in divisor
|
||||
|
||||
for (uint64_t bit = divisor; bit > 0; bit >>= 1)
|
||||
bsr++;
|
||||
|
||||
for (unsigned shift = 0; shift < bsr; shift++) {
|
||||
if (remainder >= divisor - remainder) {
|
||||
quotient = quotient * 2 + 1;
|
||||
remainder = remainder * 2 - divisor;
|
||||
}
|
||||
else {
|
||||
quotient = quotient * 2;
|
||||
remainder = remainder * 2;
|
||||
}
|
||||
}
|
||||
|
||||
return quotient;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
Copyright (c) 2019 tevador
|
||||
|
||||
This file is part of RandomX.
|
||||
|
||||
RandomX is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
RandomX is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with RandomX. If not, see<http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
uint64_t reciprocal(uint64_t);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user