Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ src/argon2_ref.c
src/argon2_ssse3.c
src/argon2_avx2.c
src/bytecode_machine.cpp
src/cpu.cpp
src/dataset.cpp
src/soft_aes.cpp
src/virtual_memory.cpp
Expand Down Expand Up @@ -132,6 +133,13 @@ if (ARM_ID STREQUAL "aarch64" OR ARM_ID STREQUAL "arm64" OR ARM_ID STREQUAL "arm
# cheat because cmake and ccache hate each other
set_property(SOURCE src/jit_compiler_a64_static.S PROPERTY LANGUAGE C)

# not sure if this check is needed
include(CheckIncludeFile)
check_include_file(asm/hwcap.h HAVE_HWCAP)
if(HAVE_HWCAP)
add_definitions(-DHAVE_HWCAP)
endif()

if(ARCH STREQUAL "native")
add_flag("-march=native")
else()
Expand Down
72 changes: 72 additions & 0 deletions src/cpu.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
Copyright (c) 2019, tevador <tevador@gmail.com>

All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "cpu.hpp"

#if defined(_M_X64) || defined(__x86_64__)
#define HAVE_CPUID
#ifdef _WIN32
#include <intrin.h>
#define cpuid(info, x) __cpuidex(info, x, 0)
#else //GCC
#include <cpuid.h>
void cpuid(int info[4], int InfoType) {
__cpuid_count(InfoType, 0, info[0], info[1], info[2], info[3]);
}
#endif
#endif

#if defined(HAVE_HWCAP)
#include <sys/auxv.h>
#include <asm/hwcap.h>
#endif

namespace randomx {

Cpu::Cpu() : aes_(false), ssse3_(false), avx2_(false) {
#ifdef HAVE_CPUID
int info[4];
cpuid(info, 0);
int nIds = info[0];
if (nIds >= 0x00000001) {
cpuid(info, 0x00000001);
ssse3_ = (info[2] & (1 << 9)) != 0;
aes_ = (info[2] & (1 << 25)) != 0;
}
if (nIds >= 0x00000007) {
cpuid(info, 0x00000007);
avx2_ = (info[1] & (1 << 5)) != 0;
}
#elif defined(__aarch64__) && defined(HWCAP_AES)
long hwcaps = getauxval(AT_HWCAP);
aes_ = (hwcaps & HWCAP_AES) != 0;
#endif
//TODO POWER8 AES
}

}
49 changes: 49 additions & 0 deletions src/cpu.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
Copyright (c) 2019, tevador <tevador@gmail.com>

All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#pragma once

namespace randomx {

class Cpu {
public:
Cpu();
bool hasAes() const {
return aes_;
}
bool hasSsse3() const {
return ssse3_;
}
bool hasAvx2() const {
return avx2_;
}
private:
bool aes_, ssse3_, avx2_;
};

}
17 changes: 5 additions & 12 deletions src/dataset.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,19 +83,12 @@ namespace randomx {
void initDataset(randomx_cache* cache, uint8_t* dataset, uint32_t startBlock, uint32_t endBlock);

inline randomx_argon2_impl* selectArgonImpl(randomx_flags flags) {
if ((flags & RANDOMX_FLAG_ARGON2) == 0) {
return &randomx_argon2_fill_segment_ref;
if (flags & RANDOMX_FLAG_ARGON2_AVX2) {
return randomx_argon2_impl_avx2();
}
randomx_argon2_impl* impl = nullptr;
if ((flags & RANDOMX_FLAG_ARGON2) == RANDOMX_FLAG_ARGON2_SSSE3) {
impl = randomx_argon2_impl_ssse3();
if (flags & RANDOMX_FLAG_ARGON2_SSSE3) {
return randomx_argon2_impl_ssse3();
}
if ((flags & RANDOMX_FLAG_ARGON2) == RANDOMX_FLAG_ARGON2_AVX2) {
impl = randomx_argon2_impl_avx2();
}
if (impl != nullptr) {
return impl;
}
throw std::runtime_error("Unsupported Argon2 implementation");
return &randomx_argon2_fill_segment_ref;
}
}
9 changes: 6 additions & 3 deletions src/intrin_portable.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ FORCE_INLINE rx_vec_f128 rx_set1_vec_f128(uint64_t x) {
#define rx_aesenc_vec_i128 _mm_aesenc_si128
#define rx_aesdec_vec_i128 _mm_aesdec_si128

#define HAVE_AES
#define HAVE_AES 1

#endif //__AES__

Expand Down Expand Up @@ -303,7 +303,7 @@ FORCE_INLINE rx_vec_i128 rx_aesdec_vec_i128(rx_vec_i128 v, rx_vec_i128 rkey) {
__m128ll out = vrev((__m128i)__builtin_crypto_vncipher(_v,zero));
return (rx_vec_i128)vec_xor((__m128i)out,rkey);
}
#define HAVE_AES
#define HAVE_AES 1

#endif //__CRYPTO__

Expand Down Expand Up @@ -455,7 +455,7 @@ FORCE_INLINE rx_vec_i128 rx_aesdec_vec_i128(rx_vec_i128 a, rx_vec_i128 key) {
return vaesimcq_u8(vaesdq_u8(a, zero)) ^ key;
}

#define HAVE_AES
#define HAVE_AES 1

#endif

Expand Down Expand Up @@ -718,6 +718,9 @@ FORCE_INLINE rx_vec_i128 rx_aesenc_vec_i128(rx_vec_i128 v, rx_vec_i128 rkey) {
FORCE_INLINE rx_vec_i128 rx_aesdec_vec_i128(rx_vec_i128 v, rx_vec_i128 rkey) {
throw std::runtime_error(platformError);
}

#define HAVE_AES 0

#endif

#ifdef RANDOMX_DEFAULT_FENV
Expand Down
26 changes: 23 additions & 3 deletions src/randomx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,38 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "vm_compiled.hpp"
#include "vm_compiled_light.hpp"
#include "blake2/blake2.h"
#include "cpu.hpp"
#include <cassert>
#include <limits>

extern "C" {

randomx_flags randomx_get_flags() {
randomx_flags flags = RANDOMX_HAVE_COMPILER ? RANDOMX_FLAG_JIT : RANDOMX_FLAG_DEFAULT;
randomx::Cpu cpu;
if (HAVE_AES && cpu.hasAes()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to wrap it into #if HAVE_AES == 1 ... #endif, no need to make it compile-time when preprocessor can handle it.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If HAVE_AES == 0, it will be optimized out during compilation. I find the code more readable this way.

flags |= RANDOMX_FLAG_HARD_AES;
}
if (randomx_argon2_impl_avx2() != nullptr && cpu.hasAvx2()) {
flags |= RANDOMX_FLAG_ARGON2_AVX2;
}
if (randomx_argon2_impl_ssse3() != nullptr && cpu.hasSsse3()) {
flags |= RANDOMX_FLAG_ARGON2_SSSE3;
}
return flags;
}

randomx_cache *randomx_alloc_cache(randomx_flags flags) {
randomx_cache *cache = nullptr;
auto impl = randomx::selectArgonImpl(flags);
if (impl == nullptr) {
return cache;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably a bad idea to return uninitialized cache here even if impl can't be nullptr with current code. It should output some error and abort, or throw an exception.

Copy link
Owner Author

@tevador tevador Oct 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. impl can be NULL if the caller selects an unsupported implementation (e.g. AVX2 on ARM).
  2. It's not returning an uninitialized cache but simply a NULL pointer. It's up to the caller to check for NULLs.
  3. This is a C-language API, so we can't throw exceptions unless we handle them internally.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then it's ok, I somehow thought cache was already allocated at this point.

}

try {
cache = new randomx_cache();
cache->argonImpl = randomx::selectArgonImpl(flags);
switch (flags & (RANDOMX_FLAG_JIT | RANDOMX_FLAG_LARGE_PAGES)) {
cache->argonImpl = impl;
switch ((int)(flags & (RANDOMX_FLAG_JIT | RANDOMX_FLAG_LARGE_PAGES))) {
case RANDOMX_FLAG_DEFAULT:
cache->dealloc = &randomx::deallocCache<randomx::DefaultAllocator>;
cache->jit = nullptr;
Expand Down Expand Up @@ -173,7 +193,7 @@ extern "C" {
randomx_vm *vm = nullptr;

try {
switch (flags & (RANDOMX_FLAG_FULL_MEM | RANDOMX_FLAG_JIT | RANDOMX_FLAG_HARD_AES | RANDOMX_FLAG_LARGE_PAGES)) {
switch ((int)(flags & (RANDOMX_FLAG_FULL_MEM | RANDOMX_FLAG_JIT | RANDOMX_FLAG_HARD_AES | RANDOMX_FLAG_LARGE_PAGES))) {
case RANDOMX_FLAG_DEFAULT:
vm = new randomx::InterpretedLightVmDefault();
break;
Expand Down
28 changes: 28 additions & 0 deletions src/randomx.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,38 @@ typedef struct randomx_dataset randomx_dataset;
typedef struct randomx_cache randomx_cache;
typedef struct randomx_vm randomx_vm;


#if defined(__cplusplus)

#ifdef __cpp_constexpr
#define CONSTEXPR constexpr
#else
#define CONSTEXPR
#endif

inline CONSTEXPR randomx_flags operator |(randomx_flags a, randomx_flags b) {
return static_cast<randomx_flags>(static_cast<int>(a) | static_cast<int>(b));
}
inline CONSTEXPR randomx_flags operator &(randomx_flags a, randomx_flags b) {
return static_cast<randomx_flags>(static_cast<int>(a) & static_cast<int>(b));
}
inline randomx_flags& operator |=(randomx_flags& a, randomx_flags b) {
return a = a | b;
}

extern "C" {
#endif

/**
* @return The recommended flags to be used on the current machine.
* Does not include:
* RANDOMX_FLAG_LARGE_PAGES
* RANDOMX_FLAG_FULL_MEM
* RANDOMX_FLAG_SECURE
* These flags must be added manually if desired.
*/
RANDOMX_EXPORT randomx_flags randomx_get_flags(void);

/**
* Creates a randomx_cache structure and allocates memory for RandomX Cache.
*
Expand Down
5 changes: 3 additions & 2 deletions src/tests/api-example1.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ int main() {
const char myInput[] = "RandomX example input";
char hash[RANDOMX_HASH_SIZE];

randomx_cache *myCache = randomx_alloc_cache(RANDOMX_FLAG_DEFAULT);
randomx_flags flags = randomx_get_flags();
randomx_cache *myCache = randomx_alloc_cache(flags);
randomx_init_cache(myCache, &myKey, sizeof myKey);
randomx_vm *myMachine = randomx_create_vm(RANDOMX_FLAG_DEFAULT, myCache, NULL);
randomx_vm *myMachine = randomx_create_vm(flags, myCache, NULL);

randomx_calculate_hash(myMachine, &myInput, sizeof myInput, hash);

Expand Down
11 changes: 7 additions & 4 deletions src/tests/api-example2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@ int main() {
const char myInput[] = "RandomX example input";
char hash[RANDOMX_HASH_SIZE];

randomx_cache *myCache = randomx_alloc_cache((randomx_flags)(RANDOMX_FLAG_JIT | RANDOMX_FLAG_LARGE_PAGES));
randomx_flags flags = randomx_get_flags();
flags |= RANDOMX_FLAG_LARGE_PAGES;
flags |= RANDOMX_FLAG_FULL_MEM;
randomx_cache *myCache = randomx_alloc_cache(flags);
if (myCache == nullptr) {
std::cout << "Cache allocation failed" << std::endl;
return 1;
}
randomx_init_cache(myCache, myKey, sizeof myKey);

randomx_dataset *myDataset = randomx_alloc_dataset(RANDOMX_FLAG_LARGE_PAGES);
randomx_dataset *myDataset = randomx_alloc_dataset(flags);
if (myDataset == nullptr) {
std::cout << "Dataset allocation failed" << std::endl;
return 1;
Expand All @@ -28,7 +31,7 @@ int main() {
t2.join();
randomx_release_cache(myCache);

randomx_vm *myMachine = randomx_create_vm((randomx_flags)(RANDOMX_FLAG_FULL_MEM | RANDOMX_FLAG_JIT | RANDOMX_FLAG_HARD_AES | RANDOMX_FLAG_LARGE_PAGES), nullptr, myDataset);
randomx_vm *myMachine = randomx_create_vm(flags, nullptr, myDataset);
if (myMachine == nullptr) {
std::cout << "Failed to create a virtual machine" << std::endl;
return 1;
Expand All @@ -40,7 +43,7 @@ int main() {
randomx_release_dataset(myDataset);

for (unsigned i = 0; i < RANDOMX_HASH_SIZE; ++i)
std::cout << std::hex << ((int)hash[i] & 0xff);
std::cout << std::hex << std::setw(2) << std::setfill('0') << ((int)hash[i] & 0xff);

std::cout << std::endl;

Expand Down
Loading