Skip to content

Commit

Permalink
Start using C++17
Browse files Browse the repository at this point in the history
  • Loading branch information
james-d-mitchell committed Oct 27, 2023
1 parent b3d55aa commit 5ce40c2
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 138 deletions.
14 changes: 1 addition & 13 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ message(STATUS "**** Build type = ${CMAKE_BUILD_TYPE}")
################################
# General compiler configuration
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_EXTENSIONS OFF) # -std=c++14 instead of -std=gnu++14

add_definitions(-DHPCOMBI_HAVE_CONFIG)
Expand All @@ -52,18 +52,6 @@ include(CheckIncludeFileCXX)
include(CheckCXXSourceCompiles)
include(CheckCXXCompilerFlag)

## Check for static lcm
check_include_file_cxx("experimental/numeric" HPCOMBI_HAVE_EXPERIMENTAL_NUMERIC)
if (HPCOMBI_HAVE_EXPERIMENTAL_NUMERIC)
check_cxx_source_compiles(
"
#include <experimental/numeric>
static_assert(std::experimental::lcm(4, 6) == 12, \"Buggy lcm\");
int main() { }
"
HPCOMBI_HAVE_EXPERIMENTAL_NUMERIC_LCM)
endif (HPCOMBI_HAVE_EXPERIMENTAL_NUMERIC)

## Check for buggy constexpr support G++ 5.0
check_cxx_source_compiles(
"
Expand Down
101 changes: 38 additions & 63 deletions include/epu.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <functional> // less<>, equal_to<>
#include <iomanip>
#include <ostream>
#include <type_traits>

#ifdef HPCOMBI_HAVE_CONFIG
#include "HPCombi-config.h"
Expand Down Expand Up @@ -61,33 +62,26 @@ static_assert(alignof(epu8) == 16,
"epu8 type is not properly aligned by the compiler !");

/// SIMD vector of 32 unsigned bytes
/// Currently not really used except in experiments
using xpu8 = uint8_t __attribute__((vector_size(32)));

namespace { // Implementation detail code

/// A handmade C++11 constexpr lambda
template <typename T> struct ConstFun {
HPCOMBI_CONSTEXPR_CONSTRUCTOR ConstFun(T cc) : cst(cc) {}
HPCOMBI_CONSTEXPR T operator()(T) const { return cst; }
/// constant value for constexpr lambda
T cst;
};
namespace detail { // Implementation detail code

/// Factory object for various SIMD constants in particular constexpr
template <class TPU> struct TPUBuild {
using type_elem =
typename std::remove_reference<decltype((TPU{})[0])>::type;
using type_elem = typename std::remove_reference_t<decltype((TPU{})[0])>;
static constexpr size_t size_elem = sizeof(type_elem);
static constexpr size_t size = sizeof(TPU) / size_elem;

using array = std::array<type_elem, size>;

template <class Fun, std::size_t... Is>
static HPCOMBI_CONSTEXPR TPU make_helper(Fun f,
std::index_sequence<Is...>) {
template <class Fun, decltype(size)... Is>
static constexpr TPU make_helper(Fun f, std::index_sequence<Is...>) {
static_assert(std::is_invocable_v<Fun, type_elem>);
return TPU{f(Is)...};
}

inline TPU operator()(const std::initializer_list<type_elem> il,
inline TPU operator()(std::initializer_list<type_elem> il,
type_elem def) const {
assert(il.size() <= size);
array res;
Expand All @@ -96,86 +90,67 @@ template <class TPU> struct TPUBuild {
return reinterpret_cast<const TPU &>(res);
}

template <class Fun> inline HPCOMBI_CONSTEXPR TPU operator()(Fun f) const {
template <class Fun> inline constexpr TPU operator()(Fun f) const {
static_assert(std::is_invocable_v<Fun, type_elem>);
return make_helper(f, std::make_index_sequence<size>{});
}

inline HPCOMBI_CONSTEXPR TPU operator()(type_elem c) const {
return make_helper(ConstFun<type_elem>(c),
inline constexpr TPU operator()(type_elem c) const {
return make_helper([c](auto) { return c; },
std::make_index_sequence<size>{});
}
// explicit overloading for int constants
inline HPCOMBI_CONSTEXPR TPU operator()(int c) const {
return operator()(uint8_t(c));
inline constexpr TPU operator()(int c) const {
return operator()(type_elem(c));
}
inline HPCOMBI_CONSTEXPR TPU operator()(size_t c) const {
return operator()(uint8_t(c));
inline constexpr TPU operator()(size_t c) const {
return operator()(type_elem(c));
}
};

// The following functions should be constexpr lambdas writen directly in
// their corresponding methods. However until C++17, constexpr lambda are
// forbidden. So we put them here.
/// The image of i by the identity function
HPCOMBI_CONSTEXPR uint8_t id_fun(uint8_t i) { return i; }
/// The image of i by the left cycle function
HPCOMBI_CONSTEXPR uint8_t left_cycle_fun(uint8_t i) { return (i + 15) % 16; }
/// The image of i by the right cycle function
HPCOMBI_CONSTEXPR
uint8_t right_cycle_fun(uint8_t i) { return (i + 1) % 16; }
/// The image of i by a left shift duplicating the hole
HPCOMBI_CONSTEXPR
uint8_t left_dup_fun(uint8_t i) { return i == 15 ? 15 : i + 1; }
/// The image of i by a right shift duplicating the hole
HPCOMBI_CONSTEXPR
uint8_t right_dup_fun(uint8_t i) { return i == 0 ? 0 : i - 1; }
/// The complement of i to 15
HPCOMBI_CONSTEXPR
uint8_t complement_fun(uint8_t i) { return 15 - i; }
HPCOMBI_CONSTEXPR uint8_t popcount4_fun(uint8_t i) {
return ((i & 1) != 0 ? 1 : 0) + ((i & 2) != 0 ? 1 : 0) +
((i & 4) != 0 ? 1 : 0) + ((i & 8) != 0 ? 1 : 0);
}

} // Anonymous namespace
} // namespace detail

/// Factory object for various SIMD constants in particular constexpr
TPUBuild<epu8> Epu8;
// Single instance of the TPUBuild<epu8> factory object
static constexpr detail::TPUBuild<epu8> Epu8;

/// The indentity #HPCombi::epu8
HPCOMBI_CONSTEXPR epu8 epu8id = Epu8(id_fun);
/// The identity #HPCombi::epu8
/// The image of i by the identity function
constexpr epu8 epu8id = Epu8([](uint8_t i) { return i; });
/// The reverted identity #HPCombi::epu8
HPCOMBI_CONSTEXPR epu8 epu8rev = Epu8(complement_fun);
constexpr epu8 epu8rev = Epu8([](uint8_t i) { return 15 - i; });
/// Left cycle #HPCombi::epu8 permutation
HPCOMBI_CONSTEXPR epu8 left_cycle = Epu8(left_cycle_fun);
constexpr epu8 left_cycle = Epu8([](uint8_t i) { return (i + 15) % 16; });
/// Right cycle #HPCombi::epu8 permutation
HPCOMBI_CONSTEXPR epu8 right_cycle = Epu8(right_cycle_fun);
constexpr epu8 right_cycle = Epu8([](uint8_t i) { return (i + 1) % 16; });
/// Left shift #HPCombi::epu8, duplicating the rightmost entry
HPCOMBI_CONSTEXPR epu8 left_dup = Epu8(left_dup_fun);
constexpr epu8 left_dup = Epu8([](uint8_t i) { return i == 15 ? 15 : i + 1; });
/// Right shift #HPCombi::epu8, duplicating the leftmost entry
HPCOMBI_CONSTEXPR epu8 right_dup = Epu8(right_dup_fun);
constexpr epu8 right_dup = Epu8([](uint8_t i) { return i == 0 ? 0 : i - 1; });
/// Popcount #HPCombi::epu8: the ith entry contains the number of bits set in i
HPCOMBI_CONSTEXPR epu8 popcount4 = Epu8(popcount4_fun);
constexpr epu8 popcount4 = Epu8([](uint8_t i) {
return ((i & 1) != 0 ? 1 : 0) + ((i & 2) != 0 ? 1 : 0) +
((i & 4) != 0 ? 1 : 0) + ((i & 8) != 0 ? 1 : 0);
});

/** Cast a #HPCombi::epu8 to a c++ \c std::array
*
* This is usually faster for algorithm using a lot of indexed acces.
*/
inline TPUBuild<epu8>::array &as_array(epu8 &v) {
return reinterpret_cast<typename TPUBuild<epu8>::array &>(v);
inline decltype(Epu8)::array &as_array(epu8 &v) {
return reinterpret_cast<decltype(Epu8)::array &>(v);
}
/** Cast a constant #HPCombi::epu8 to a C++ \c std::array
*
* This is usually faster for algorithm using a lot of indexed acces.
*/
inline const TPUBuild<epu8>::array &as_array(const epu8 &v) {
return reinterpret_cast<const typename TPUBuild<epu8>::array &>(v);
inline const decltype(Epu8)::array &as_array(const epu8 &v) {
return reinterpret_cast<const decltype(Epu8)::array &>(v);
}
/** Cast a C++ \c std::array to a #HPCombi::epu8 */
// Passing the argument by reference triggers a segfault in gcc
// Since vector types doesn't belongs to the standard, I didn't manage
// to know if I'm using undefined behavior here.
inline epu8 from_array(TPUBuild<epu8>::array a) {
inline epu8 from_array(decltype(Epu8)::array a) {
return reinterpret_cast<const epu8 &>(a);
}

Expand Down Expand Up @@ -285,7 +260,7 @@ inline epu8 permutation_of_ref(epu8 a, epu8 b);
inline epu8 permutation_of(epu8 a, epu8 b);

/** A prime number good for hashing */
const uint64_t prime = 0x9e3779b97f4a7bb9;
constexpr uint64_t prime = 0x9e3779b97f4a7bb9;

/** A random #HPCombi::epu8
* @details
Expand Down
14 changes: 8 additions & 6 deletions include/epu_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// http://www.gnu.org/licenses/ //
//****************************************************************************//

// This is the implementation par of epu.hpp this should be seen as
// This is the implementation part of epu.hpp this should be seen as
// implementation details and should not be included directly.

#include <initializer_list>
Expand Down Expand Up @@ -113,11 +113,11 @@ inline uint64_t last_non_zero(epu8 v, int bnd) {
}

/// Apply a sorting network
template <bool Increassing = true, size_t sz>
template <bool Increasing = true, size_t sz>
inline epu8 network_sort(epu8 res, std::array<epu8, sz> rounds) {
for (auto round : rounds) {
// This conditional should be optimized out by the compiler
epu8 mask = Increassing ? round < epu8id : epu8id < round;
epu8 mask = Increasing ? round < epu8id : epu8id < round;
epu8 b = permuted(res, round);
// res = mask ? min(res,b) : max(res,b); is not accepted by clang
res = simde_mm_blendv_epi8(min(res, b), max(res, b), mask);
Expand All @@ -126,12 +126,12 @@ inline epu8 network_sort(epu8 res, std::array<epu8, sz> rounds) {
}

/// Apply a sorting network in place and return the permutation
template <bool Increassing = true, size_t sz>
template <bool Increasing = true, size_t sz>
inline epu8 network_sort_perm(epu8 &v, std::array<epu8, sz> rounds) {
epu8 res = epu8id;
for (auto round : rounds) {
// This conditional should be optimized out by the compiler
epu8 mask = Increassing ? round < epu8id : epu8id < round;
epu8 mask = Increasing ? round < epu8id : epu8id < round;
epu8 b = permuted(v, round);
epu8 cmp = simde_mm_blendv_epi8(b < v, v < b, mask);
v = simde_mm_blendv_epi8(v, b, cmp);
Expand Down Expand Up @@ -229,6 +229,7 @@ constexpr std::array<epu8, 3> inverting_rounds{{
SIMDE_SIDD_NEGATIVE_POLARITY)
#define FIND_IN_VECT_COMPL \
(SIMDE_SIDD_UBYTE_OPS | SIMDE_SIDD_CMP_EQUAL_ANY | SIMDE_SIDD_UNIT_MASK)

inline epu8 permutation_of_cmpestrm(epu8 a, epu8 b) {
epu8 res = -static_cast<epu8>(_mm_cmpestrm(a, 8, b, 16, FIND_IN_VECT));
for (epu8 round : inverting_rounds) {
Expand Down Expand Up @@ -391,8 +392,9 @@ inline epu8 eval16_ref(epu8 v) {
res[v[i]]++;
return res;
}

inline epu8 eval16_arr(epu8 v8) {
TPUBuild<epu8>::array res{};
decltype(Epu8)::array res{};
auto v = as_array(v8);
for (size_t i = 0; i < 16; i++)
if (v[i] < 16)
Expand Down
35 changes: 0 additions & 35 deletions include/fallback/gcdlcm.hpp

This file was deleted.

2 changes: 1 addition & 1 deletion include/perm16.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ struct alignas(16) PTransf16 : public Vect16 {
static constexpr size_t size() { return 16; }

using vect = HPCombi::Vect16;
using array = TPUBuild<epu8>::array;
using array = decltype(Epu8)::array;

PTransf16() = default;
HPCOMBI_CONSTEXPR_CONSTRUCTOR PTransf16(const PTransf16 &v) = default;
Expand Down
30 changes: 11 additions & 19 deletions include/perm16_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,12 @@
// http://www.gnu.org/licenses/ //
//****************************************************************************//

#include "power.hpp"
#include <algorithm>
#include <iomanip>
#include <numeric>
#include <random>

#ifdef HAVE_EXPERIMENTAL_NUMERIC_LCM
#include <experimental/numeric> // lcm until c++17
#else
#include "fallback/gcdlcm.hpp" // lcm until c++17
#endif // HAVE_EXPERIMENTAL_NUMERIC_LCM
#include "power.hpp"

namespace HPCombi {

Expand Down Expand Up @@ -71,8 +67,8 @@ inline PTransf16 PTransf16::left_one() const {
return image_mask(true) | epu8id;
}
inline uint32_t PTransf16::rank_ref() const {
TPUBuild<epu8>::array tmp{};
static_assert(TPUBuild<epu8>::size == 16, "Wrong size of EPU8 array");
decltype(Epu8)::array tmp{};
static_assert(decltype(Epu8)::size == 16, "Wrong size of EPU8 array");
for (size_t i = 0; i < 16; i++) {
if (v[i] != 0xFF)
tmp[v[i]] = 1;
Expand Down Expand Up @@ -249,15 +245,11 @@ inline Perm16 Perm16::inverse_cycl() const {
return res;
}

static constexpr unsigned lcm_range(unsigned n) {
#if __cplusplus <= 201103L
return n == 1 ? 1 : std::experimental::lcm(lcm_range(n - 1), n);
#else
unsigned res = 1;
for (unsigned i = 1; i <= n; ++i)
res = std::experimental::lcm(res, i);
static constexpr uint32_t lcm_range(uint8_t n) {
uint32_t res = 1;
for (uint8_t i = 1; i <= n; ++i)
res = std::lcm(res, i);
return res;
#endif
}

inline Perm16 Perm16::inverse_pow() const {
Expand All @@ -274,8 +266,8 @@ inline epu8 Perm16::lehmer_ref() const {
}

inline epu8 Perm16::lehmer_arr() const {
TPUBuild<epu8>::array res{};
TPUBuild<epu8>::array ar = as_array();
decltype(Epu8)::array res{};
decltype(Epu8)::array ar = as_array();
for (size_t i = 0; i < 16; i++)
for (size_t j = i + 1; j < 16; j++)
if (ar[i] > ar[j])
Expand Down Expand Up @@ -303,7 +295,7 @@ inline uint8_t Perm16::length_ref() const {

inline uint8_t Perm16::length_arr() const {
uint8_t res = 0;
TPUBuild<epu8>::array ar = as_array();
decltype(Epu8)::array ar = as_array();
for (size_t i = 0; i < 16; i++)
for (size_t j = i + 1; j < 16; j++)
if (ar[i] > ar[j])
Expand Down
2 changes: 1 addition & 1 deletion include/vect16.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace HPCombi {

struct alignas(16) Vect16 {
static constexpr size_t Size() { return 16; }
using array = TPUBuild<epu8>::array;
using array = decltype(Epu8)::array;
epu8 v;

Vect16() = default;
Expand Down

0 comments on commit 5ce40c2

Please sign in to comment.