Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

clearing bit utils #2301

Closed
Closed
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
3 changes: 2 additions & 1 deletion folly/algorithm/simd/FindFixed.h
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,8 @@ constexpr std::optional<std::size_t> findFixed(std::span<const T, N> where, U x)
return find_fixed_detail::findFixedConstexpr(std::span<const T>(where), x);
} else {
return find_fixed_detail::findFixedDispatch(
detail::asSimdFriendlyUint(where), detail::asSimdFriendlyUint(x));
simd::detail::asSimdFriendlyUint(where),
simd::detail::asSimdFriendlyUint(x));
}
}

Expand Down
1 change: 1 addition & 0 deletions folly/algorithm/simd/detail/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ cpp_library(
name = "traits",
headers = ["Traits.h"],
exported_deps = [
"//folly:c_portability",
"//folly:memory",
"//folly:traits",
"//folly/container:span",
Expand Down
107 changes: 59 additions & 48 deletions folly/algorithm/simd/detail/Traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@

#pragma once

#include <folly/CPortability.h>
#include <folly/Memory.h>
#include <folly/Traits.h>
#include <folly/container/span.h>

#include <concepts>
#include <type_traits>

namespace folly::detail {
namespace folly::simd::detail {

template <typename T>
auto findSimdFriendlyEquivalent() {
Expand All @@ -36,65 +37,75 @@ auto findSimdFriendlyEquivalent() {
return double{};
}
} else if constexpr (std::is_signed_v<T>) {
if constexpr (sizeof(T) == 1) {
return std::int8_t{};
} else if constexpr (sizeof(T) == 2) {
return std::int16_t{};
} else if constexpr (sizeof(T) == 4) {
return std::int32_t{};
} else if constexpr (sizeof(T) == 8) {
return std::int64_t{};
}
return int_bits_t<sizeof(T) * 8>{};
} else if constexpr (std::is_unsigned_v<T>) {
if constexpr (sizeof(T) == 1) {
return std::uint8_t{};
} else if constexpr (sizeof(T) == 2) {
return std::uint16_t{};
} else if constexpr (sizeof(T) == 4) {
return std::uint32_t{};
} else if constexpr (sizeof(T) == 8) {
return std::uint64_t{};
}
return uint_bits_t<sizeof(T) * 8>{};
}
}

template <typename T>
concept has_simd_friendly_equivalent =
constexpr bool has_simd_friendly_equivalent_scalar =
!std::is_same_v<void, decltype(findSimdFriendlyEquivalent<T>())>;

template <has_simd_friendly_equivalent T>
using simd_friendly_equivalent_t = folly::like_t< //
T,
decltype(findSimdFriendlyEquivalent<std::remove_const_t<T>>())>;
template <typename T>
using simd_friendly_equivalent_scalar_t = std::enable_if_t<
has_simd_friendly_equivalent_scalar<T>,
like_t<T, decltype(findSimdFriendlyEquivalent<std::remove_const_t<T>>())>>;

template <typename T>
concept has_integral_simd_friendly_equivalent =
has_simd_friendly_equivalent<T> && // have to explicitly specify this for
// subsumption to work
std::integral<simd_friendly_equivalent_t<T>>;
constexpr bool has_integral_simd_friendly_equivalent_scalar =
std::is_integral_v< // void will return false
decltype(findSimdFriendlyEquivalent<std::remove_const_t<T>>())>;

template <has_integral_simd_friendly_equivalent T>
using integral_simd_friendly_equivalent = simd_friendly_equivalent_t<T>;
template <typename T>
using unsigned_simd_friendly_equivalent_scalar_t = std::enable_if_t<
has_integral_simd_friendly_equivalent_scalar<T>,
like_t<T, uint_bits_t<sizeof(T) * 8>>>;

template <has_simd_friendly_equivalent T, std::size_t Extend>
auto asSimdFriendly(folly::span<T, Extend> s) {
return folly::reinterpret_span_cast<simd_friendly_equivalent_t<T>>(s);
}
template <typename R>
using span_for = decltype(folly::span(std::declval<const R&>()));

template <has_simd_friendly_equivalent T>
constexpr auto asSimdFriendly(T x) {
return static_cast<simd_friendly_equivalent_t<T>>(x);
}
struct AsSimdFriendlyFn {
template <typename T, std::size_t extent>
FOLLY_ERASE auto operator()(folly::span<T, extent> s) const
-> folly::span<simd_friendly_equivalent_scalar_t<T>, extent> {
return reinterpret_span_cast<simd_friendly_equivalent_scalar_t<T>>(s);
}

template <has_simd_friendly_equivalent T, std::size_t Extend>
auto asSimdFriendlyUint(folly::span<T, Extend> s) {
return folly::reinterpret_span_cast<
folly::like_t<T, uint_bits_t<sizeof(T) * 8>>>(s);
}
template <typename R>
FOLLY_ERASE auto operator()(R&& r) const
-> decltype(operator()(span_for<R>(r))) {
return operator()(folly::span(r));
}

template <has_simd_friendly_equivalent T>
constexpr auto asSimdFriendlyUint(T x) {
return static_cast<uint_bits_t<sizeof(T) * 8>>(x);
}
template <typename T>
FOLLY_ERASE constexpr auto operator()(T x) const
-> simd_friendly_equivalent_scalar_t<T> {
return static_cast<simd_friendly_equivalent_scalar_t<T>>(x);
}
};
inline constexpr AsSimdFriendlyFn asSimdFriendly;

struct AsSimdFriendlyUintFn {
template <typename T, std::size_t extent>
FOLLY_ERASE auto operator()(folly::span<T, extent> s) const
-> folly::span<unsigned_simd_friendly_equivalent_scalar_t<T>, extent> {
return reinterpret_span_cast<unsigned_simd_friendly_equivalent_scalar_t<T>>(
s);
}

template <typename R>
FOLLY_ERASE auto operator()(R&& r) const
-> decltype(operator()(span_for<R>(r))) {
return operator()(folly::span(r));
}

template <typename T>
FOLLY_ERASE constexpr auto operator()(T x) const
-> unsigned_simd_friendly_equivalent_scalar_t<T> {
return static_cast<unsigned_simd_friendly_equivalent_scalar_t<T>>(x);
}
};
inline constexpr AsSimdFriendlyUintFn asSimdFriendlyUint;

} // namespace folly::detail
} // namespace folly::simd::detail
1 change: 1 addition & 0 deletions folly/algorithm/simd/detail/test/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ cpp_unittest(
cpp_unittest(
name = "traits_test",
srcs = ["TraitsTest.cpp"],
compiler_flags = ["--std=c++17"],
deps = [
"//folly/algorithm/simd/detail:traits",
"//folly/portability:gmock",
Expand Down
181 changes: 113 additions & 68 deletions folly/algorithm/simd/detail/test/TraitsTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,84 +19,147 @@
#include <folly/portability/GMock.h>
#include <folly/portability/GTest.h>

namespace folly::detail {
#include <set>
#include <vector>

namespace folly::simd::detail {

struct FollySimdTraitsTest : testing::Test {};

namespace simd_friendly_equivalent_test {
namespace simd_friendly_equivalent_scalar_test {

// ints
static_assert(
std::is_same_v<std::int8_t, simd_friendly_equivalent_t<signed char>>);
static_assert(
std::is_same_v<std::uint8_t, simd_friendly_equivalent_t<unsigned char>>);
static_assert(std::is_same_v<
std::int8_t,
simd_friendly_equivalent_scalar_t<signed char>>);
static_assert(std::is_same_v<
std::uint8_t,
simd_friendly_equivalent_scalar_t<unsigned char>>);

static_assert(std::is_same_v<std::int16_t, simd_friendly_equivalent_t<short>>);
static_assert(
std::is_same_v<std::uint16_t, simd_friendly_equivalent_t<unsigned short>>);
std::is_same_v<std::int16_t, simd_friendly_equivalent_scalar_t<short>>);
static_assert(std::is_same_v<
std::uint16_t,
simd_friendly_equivalent_scalar_t<unsigned short>>);

static_assert(std::is_same_v<std::int32_t, simd_friendly_equivalent_t<int>>);
static_assert(
std::is_same_v<std::uint32_t, simd_friendly_equivalent_t<unsigned int>>);

static_assert(
std::is_same_v<std::int64_t, simd_friendly_equivalent_t<std::int64_t>>);
static_assert(
std::is_same_v<std::uint64_t, simd_friendly_equivalent_t<std::uint64_t>>);
std::is_same_v<std::int32_t, simd_friendly_equivalent_scalar_t<int>>);
static_assert(std::is_same_v<
std::uint32_t,
simd_friendly_equivalent_scalar_t<unsigned int>>);

static_assert(std::is_same_v<
std::int64_t,
simd_friendly_equivalent_scalar_t<std::int64_t>>);
static_assert(std::is_same_v<
std::uint64_t,
simd_friendly_equivalent_scalar_t<std::uint64_t>>);

// floats
static_assert(std::is_same_v<float, simd_friendly_equivalent_t<float>>);
static_assert(std::is_same_v<double, simd_friendly_equivalent_t<double>>);
static_assert(std::is_same_v<float, simd_friendly_equivalent_scalar_t<float>>);
static_assert(
std::is_same_v<double, simd_friendly_equivalent_scalar_t<double>>);

// enum
enum SomeInt {};
enum class SomeIntClass : std::int32_t {};

static_assert(
std::is_same_v<std::uint32_t, simd_friendly_equivalent_t<SomeInt>>);
static_assert(
std::is_same_v<std::int32_t, simd_friendly_equivalent_t<SomeIntClass>>);
std::is_same_v<std::uint32_t, simd_friendly_equivalent_scalar_t<SomeInt>>);
static_assert(std::is_same_v<
std::int32_t,
simd_friendly_equivalent_scalar_t<SomeIntClass>>);

// const

static_assert(
std::is_same_v<const std::int32_t, simd_friendly_equivalent_t<const int>>);
static_assert(std::is_same_v<
const std::int32_t,
simd_friendly_equivalent_scalar_t<const int>>);

// sfinae
constexpr auto sfinae_call =
[]<typename T>(T) -> simd_friendly_equivalent_t<T> { return {}; };

static_assert(std::invocable<decltype(sfinae_call), int>);
struct sfinae_call {
template <typename T>
simd_friendly_equivalent_scalar_t<T> operator()(T) const {
return {};
}
};

static_assert(std::is_invocable_v<sfinae_call, int>);

struct NotSimdFriendly {};
static_assert(!std::invocable<decltype(sfinae_call), NotSimdFriendly>);
static_assert(!std::is_invocable_v<sfinae_call, NotSimdFriendly>);

} // namespace simd_friendly_equivalent_test
} // namespace simd_friendly_equivalent_scalar_test

namespace integral_simd_friendly_equivalent_test {
namespace as_simd_friendly_type_test {

static_assert(std::is_same_v< //
std::int8_t,
integral_simd_friendly_equivalent<signed char>>);
template <typename T>
using asSimdFriendlyResult = std::invoke_result_t<AsSimdFriendlyFn, T>;

struct Overloading {
constexpr int operator()(auto) { return 0; }
constexpr int operator()(has_simd_friendly_equivalent auto) { return 1; }
constexpr int operator()(has_integral_simd_friendly_equivalent auto) {
return 2;
}
};
static_assert(std::is_same_v<
folly::span<std::int32_t>,
asSimdFriendlyResult<folly::span<std::int32_t>>>);

// Subsumption tests
struct NotSimdFriendly {};
enum class SomeInt {};
static_assert(std::is_same_v<
folly::span<std::int32_t>,
asSimdFriendlyResult<folly::span<int>>>);

static_assert(std::is_same_v<
folly::span<std::int32_t>,
asSimdFriendlyResult<std::vector<int>&>>);

static_assert(std::is_same_v<
folly::span<const std::int32_t>,
asSimdFriendlyResult<const std::vector<int>&>>);

static_assert(std::is_same_v<
folly::span<const double>,
asSimdFriendlyResult<const std::vector<double>&>>);

static_assert(std::is_same_v<double, asSimdFriendlyResult<double>>);

static_assert(Overloading{}(NotSimdFriendly{}) == 0);
static_assert(Overloading{}(float{}) == 1);
static_assert(Overloading{}(int{}) == 2);
static_assert(Overloading{}(SomeInt{}) == 2);
static_assert(!std::is_invocable_v<AsSimdFriendlyFn, std::set<int>>);

} // namespace integral_simd_friendly_equivalent_test
} // namespace as_simd_friendly_type_test

namespace as_simd_friendly_uint_type_test {

template <typename T>
using asSimdFriendlyUintResult = std::invoke_result_t<AsSimdFriendlyUintFn, T>;

static_assert(std::is_same_v<
folly::span<std::uint32_t>,
asSimdFriendlyUintResult<folly::span<std::uint32_t>>>);

static_assert(std::is_same_v<
folly::span<std::uint32_t>,
asSimdFriendlyUintResult<folly::span<std::int32_t>>>);

static_assert(std::is_same_v<
folly::span<std::uint32_t>,
asSimdFriendlyUintResult<folly::span<int>>>);

static_assert(std::is_same_v<
folly::span<std::uint32_t>,
asSimdFriendlyUintResult<std::vector<int>&>>);

static_assert(std::is_same_v<
folly::span<const std::uint32_t>,
asSimdFriendlyUintResult<const std::vector<std::uint32_t>&>>);

static_assert(
std::is_same_v<std::uint32_t, asSimdFriendlyUintResult<std::int32_t>>);

static_assert(
!std::is_invocable_v<AsSimdFriendlyUintFn, const std::vector<double>&>);

static_assert(
!std::is_invocable_v<AsSimdFriendlyUintFn, const std::vector<double>&>);

static_assert(!std::is_invocable_v<AsSimdFriendlyUintFn, std::set<int>>);

} // namespace as_simd_friendly_uint_type_test

TEST_F(FollySimdTraitsTest, AsSimdFriendly) {
enum SomeEnum : int { Foo = 1, Bar, Baz };
Expand All @@ -108,32 +171,14 @@ TEST_F(FollySimdTraitsTest, AsSimdFriendly) {
ASSERT_THAT(castSpan, testing::ElementsAre(1, 2, 3));
}

template <typename T, typename U>
void isSameTest(const T&, const U&) = delete;

template <typename T>
void isSameTest(const T&, const T&) {}

template <typename From, typename To>
void asSimdFriendlyUintTypeTest() {
isSameTest(asSimdFriendlyUint(From{}), To{});
isSameTest(asSimdFriendlyUint(std::span<From>{}), std::span<To>{});
isSameTest(
asSimdFriendlyUint(std::span<const From>{}), std::span<const To>{});
}

TEST_F(FollySimdTraitsTest, AsSimdFriendlyUint) {
enum SomeEnum : int { Foo = 1, Bar, Baz };

static_assert(asSimdFriendlyUint(SomeEnum::Foo) == 1U);

asSimdFriendlyUintTypeTest<char, std::uint8_t>();
asSimdFriendlyUintTypeTest<short, std::uint16_t>();
asSimdFriendlyUintTypeTest<int, std::uint32_t>();
asSimdFriendlyUintTypeTest<unsigned, std::uint32_t>();
asSimdFriendlyUintTypeTest<float, std::uint32_t>();
asSimdFriendlyUintTypeTest<int64_t, std::uint64_t>();
asSimdFriendlyUintTypeTest<double, std::uint64_t>();
std::array arr{SomeEnum::Foo, SomeEnum::Bar, SomeEnum::Baz};
folly::span<std::uint32_t, 3> castSpan = asSimdFriendlyUint(folly::span(arr));
ASSERT_THAT(castSpan, testing::ElementsAre(1, 2, 3));
}

} // namespace folly::detail
} // namespace folly::simd::detail
Loading
Loading