diff --git a/folly/algorithm/simd/FindFixed.h b/folly/algorithm/simd/FindFixed.h index 547b02a6b3a..455799ad206 100644 --- a/folly/algorithm/simd/FindFixed.h +++ b/folly/algorithm/simd/FindFixed.h @@ -293,7 +293,8 @@ constexpr std::optional findFixed(std::span where, U x) return find_fixed_detail::findFixedConstexpr(std::span(where), x); } else { return find_fixed_detail::findFixedDispatch( - detail::asSimdFriendlyUint(where), detail::asSimdFriendlyUint(x)); + simd::detail::asSimdFriendlyUint(where), + simd::detail::asSimdFriendlyUint(x)); } } diff --git a/folly/algorithm/simd/detail/BUCK b/folly/algorithm/simd/detail/BUCK index 53a172a2e6b..7a347fb5333 100644 --- a/folly/algorithm/simd/detail/BUCK +++ b/folly/algorithm/simd/detail/BUCK @@ -40,6 +40,7 @@ cpp_library( name = "traits", headers = ["Traits.h"], exported_deps = [ + "//folly:c_portability", "//folly:memory", "//folly:traits", "//folly/container:span", diff --git a/folly/algorithm/simd/detail/Traits.h b/folly/algorithm/simd/detail/Traits.h index ee2ef3d5105..f3e34cb7eee 100644 --- a/folly/algorithm/simd/detail/Traits.h +++ b/folly/algorithm/simd/detail/Traits.h @@ -16,6 +16,7 @@ #pragma once +#include #include #include #include @@ -23,7 +24,7 @@ #include #include -namespace folly::detail { +namespace folly::simd::detail { template auto findSimdFriendlyEquivalent() { @@ -36,65 +37,75 @@ auto findSimdFriendlyEquivalent() { return double{}; } } else if constexpr (std::is_signed_v) { - 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{}; } else if constexpr (std::is_unsigned_v) { - 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{}; } } template -concept has_simd_friendly_equivalent = +constexpr bool has_simd_friendly_equivalent_scalar = !std::is_same_v())>; -template -using simd_friendly_equivalent_t = folly::like_t< // - T, - decltype(findSimdFriendlyEquivalent>())>; +template +using simd_friendly_equivalent_scalar_t = std::enable_if_t< + has_simd_friendly_equivalent_scalar, + like_t>())>>; template -concept has_integral_simd_friendly_equivalent = - has_simd_friendly_equivalent && // have to explicitly specify this for - // subsumption to work - std::integral>; +constexpr bool has_integral_simd_friendly_equivalent_scalar = + std::is_integral_v< // void will return false + decltype(findSimdFriendlyEquivalent>())>; -template -using integral_simd_friendly_equivalent = simd_friendly_equivalent_t; +template +using unsigned_simd_friendly_equivalent_scalar_t = std::enable_if_t< + has_integral_simd_friendly_equivalent_scalar, + like_t>>; -template -auto asSimdFriendly(folly::span s) { - return folly::reinterpret_span_cast>(s); -} +template +using span_for = decltype(folly::span(std::declval())); -template -constexpr auto asSimdFriendly(T x) { - return static_cast>(x); -} +struct AsSimdFriendlyFn { + template + FOLLY_ERASE auto operator()(folly::span s) const + -> folly::span, extent> { + return reinterpret_span_cast>(s); + } -template -auto asSimdFriendlyUint(folly::span s) { - return folly::reinterpret_span_cast< - folly::like_t>>(s); -} + template + FOLLY_ERASE auto operator()(R&& r) const + -> decltype(operator()(span_for(r))) { + return operator()(folly::span(r)); + } -template -constexpr auto asSimdFriendlyUint(T x) { - return static_cast>(x); -} + template + FOLLY_ERASE constexpr auto operator()(T x) const + -> simd_friendly_equivalent_scalar_t { + return static_cast>(x); + } +}; +inline constexpr AsSimdFriendlyFn asSimdFriendly; + +struct AsSimdFriendlyUintFn { + template + FOLLY_ERASE auto operator()(folly::span s) const + -> folly::span, extent> { + return reinterpret_span_cast>( + s); + } + + template + FOLLY_ERASE auto operator()(R&& r) const + -> decltype(operator()(span_for(r))) { + return operator()(folly::span(r)); + } + + template + FOLLY_ERASE constexpr auto operator()(T x) const + -> unsigned_simd_friendly_equivalent_scalar_t { + return static_cast>(x); + } +}; +inline constexpr AsSimdFriendlyUintFn asSimdFriendlyUint; -} // namespace folly::detail +} // namespace folly::simd::detail diff --git a/folly/algorithm/simd/detail/test/BUCK b/folly/algorithm/simd/detail/test/BUCK index 6661c2ce012..dea91d505cf 100644 --- a/folly/algorithm/simd/detail/test/BUCK +++ b/folly/algorithm/simd/detail/test/BUCK @@ -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", diff --git a/folly/algorithm/simd/detail/test/TraitsTest.cpp b/folly/algorithm/simd/detail/test/TraitsTest.cpp index 5d01d9630df..23247b0c6f5 100644 --- a/folly/algorithm/simd/detail/test/TraitsTest.cpp +++ b/folly/algorithm/simd/detail/test/TraitsTest.cpp @@ -19,84 +19,147 @@ #include #include -namespace folly::detail { +#include +#include + +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>); -static_assert( - std::is_same_v>); +static_assert(std::is_same_v< + std::int8_t, + simd_friendly_equivalent_scalar_t>); +static_assert(std::is_same_v< + std::uint8_t, + simd_friendly_equivalent_scalar_t>); -static_assert(std::is_same_v>); static_assert( - std::is_same_v>); + std::is_same_v>); +static_assert(std::is_same_v< + std::uint16_t, + simd_friendly_equivalent_scalar_t>); -static_assert(std::is_same_v>); static_assert( - std::is_same_v>); - -static_assert( - std::is_same_v>); -static_assert( - std::is_same_v>); + std::is_same_v>); +static_assert(std::is_same_v< + std::uint32_t, + simd_friendly_equivalent_scalar_t>); + +static_assert(std::is_same_v< + std::int64_t, + simd_friendly_equivalent_scalar_t>); +static_assert(std::is_same_v< + std::uint64_t, + simd_friendly_equivalent_scalar_t>); // floats -static_assert(std::is_same_v>); -static_assert(std::is_same_v>); +static_assert(std::is_same_v>); +static_assert( + std::is_same_v>); // enum enum SomeInt {}; enum class SomeIntClass : std::int32_t {}; static_assert( - std::is_same_v>); -static_assert( - std::is_same_v>); + std::is_same_v>); +static_assert(std::is_same_v< + std::int32_t, + simd_friendly_equivalent_scalar_t>); // const -static_assert( - std::is_same_v>); +static_assert(std::is_same_v< + const std::int32_t, + simd_friendly_equivalent_scalar_t>); // sfinae -constexpr auto sfinae_call = - [](T) -> simd_friendly_equivalent_t { return {}; }; -static_assert(std::invocable); +struct sfinae_call { + template + simd_friendly_equivalent_scalar_t operator()(T) const { + return {}; + } +}; + +static_assert(std::is_invocable_v); struct NotSimdFriendly {}; -static_assert(!std::invocable); +static_assert(!std::is_invocable_v); -} // 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>); +template +using asSimdFriendlyResult = std::invoke_result_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, + asSimdFriendlyResult>>); -// Subsumption tests -struct NotSimdFriendly {}; -enum class SomeInt {}; +static_assert(std::is_same_v< + folly::span, + asSimdFriendlyResult>>); + +static_assert(std::is_same_v< + folly::span, + asSimdFriendlyResult&>>); + +static_assert(std::is_same_v< + folly::span, + asSimdFriendlyResult&>>); + +static_assert(std::is_same_v< + folly::span, + asSimdFriendlyResult&>>); + +static_assert(std::is_same_v>); -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>); -} // namespace integral_simd_friendly_equivalent_test +} // namespace as_simd_friendly_type_test + +namespace as_simd_friendly_uint_type_test { + +template +using asSimdFriendlyUintResult = std::invoke_result_t; + +static_assert(std::is_same_v< + folly::span, + asSimdFriendlyUintResult>>); + +static_assert(std::is_same_v< + folly::span, + asSimdFriendlyUintResult>>); + +static_assert(std::is_same_v< + folly::span, + asSimdFriendlyUintResult>>); + +static_assert(std::is_same_v< + folly::span, + asSimdFriendlyUintResult&>>); + +static_assert(std::is_same_v< + folly::span, + asSimdFriendlyUintResult&>>); + +static_assert( + std::is_same_v>); + +static_assert( + !std::is_invocable_v&>); + +static_assert( + !std::is_invocable_v&>); + +static_assert(!std::is_invocable_v>); + +} // namespace as_simd_friendly_uint_type_test TEST_F(FollySimdTraitsTest, AsSimdFriendly) { enum SomeEnum : int { Foo = 1, Bar, Baz }; @@ -108,32 +171,14 @@ TEST_F(FollySimdTraitsTest, AsSimdFriendly) { ASSERT_THAT(castSpan, testing::ElementsAre(1, 2, 3)); } -template -void isSameTest(const T&, const U&) = delete; - -template -void isSameTest(const T&, const T&) {} - -template -void asSimdFriendlyUintTypeTest() { - isSameTest(asSimdFriendlyUint(From{}), To{}); - isSameTest(asSimdFriendlyUint(std::span{}), std::span{}); - isSameTest( - asSimdFriendlyUint(std::span{}), std::span{}); -} - TEST_F(FollySimdTraitsTest, AsSimdFriendlyUint) { enum SomeEnum : int { Foo = 1, Bar, Baz }; static_assert(asSimdFriendlyUint(SomeEnum::Foo) == 1U); - asSimdFriendlyUintTypeTest(); - asSimdFriendlyUintTypeTest(); - asSimdFriendlyUintTypeTest(); - asSimdFriendlyUintTypeTest(); - asSimdFriendlyUintTypeTest(); - asSimdFriendlyUintTypeTest(); - asSimdFriendlyUintTypeTest(); + std::array arr{SomeEnum::Foo, SomeEnum::Bar, SomeEnum::Baz}; + folly::span castSpan = asSimdFriendlyUint(folly::span(arr)); + ASSERT_THAT(castSpan, testing::ElementsAre(1, 2, 3)); } -} // namespace folly::detail +} // namespace folly::simd::detail diff --git a/folly/container/BUCK b/folly/container/BUCK index 528f8b81fad..61ab6de5431 100644 --- a/folly/container/BUCK +++ b/folly/container/BUCK @@ -172,11 +172,12 @@ cpp_library( name = "span", headers = ["span.h"], exported_deps = [ + ":access", + ":iterator", "//folly:cpp_attributes", "//folly:portability", "//folly:traits", "//folly:utility", - "//folly/container:access", "//folly/functional:invoke", "//folly/portability:constexpr", ], diff --git a/folly/container/Iterator.h b/folly/container/Iterator.h index 68e1baacf9a..bfecb51e753 100644 --- a/folly/container/Iterator.h +++ b/folly/container/Iterator.h @@ -112,6 +112,12 @@ inline constexpr bool iterator_category_matches_v = template using iterator_value_type_t = typename std::iterator_traits::value_type; +// iterator_reference_type_t +// +// Extracts reference from an iterator (C++20 iter_reference_t backported) +template +using iterator_reference_t = decltype(*std::declval()); + // iterator_key_type_t // // Extracts a key type from an iterator, leverages the knowledge that diff --git a/folly/container/span.h b/folly/container/span.h index f326bcf0cbf..50aa4eb7468 100644 --- a/folly/container/span.h +++ b/folly/container/span.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -247,6 +248,22 @@ class span { } }; +template +span(T*, EndOrSize) -> span; + +template +span(T (&)[N]) -> span; + +template +span(std::array&) -> span; + +template +span(const std::array&) -> span; + +template +span(R&&) -> span()))>>>; + } // namespace fallback_span } // namespace detail diff --git a/folly/container/test/span_test.cpp b/folly/container/test/span_test.cpp index 226c7d05cee..5d09f165391 100644 --- a/folly/container/test/span_test.cpp +++ b/folly/container/test/span_test.cpp @@ -324,6 +324,46 @@ TYPED_TEST_P(SpanTest, fix_as_bytes) { EXPECT_EQ(20, wbytes.size()); } +namespace fallback_span_ctad { + +namespace fallback = folly::detail::fallback_span; + +template +using deduced_for = decltype(fallback::span(std::declval()...)); + +static_assert( // + std::is_same_v< + fallback::span, + deduced_for>); + +static_assert( // + std::is_same_v< + fallback::span, + deduced_for&>>); + +static_assert( // + std::is_same_v, deduced_for&>>); + +static_assert( + std::is_same_v, deduced_for&>>); + +static_assert( // + std::is_same_v< + fallback::span, + deduced_for&>>); + +int arr1[3]; +static_assert( // + std::is_same_v, decltype(fallback::span(arr1))>); + +constexpr int arr2[3]{0, 1, 2}; +static_assert( // + std::is_same_v< + fallback::span, + decltype(fallback::span(arr2))>); + +} // namespace fallback_span_ctad + // clang-format off REGISTER_TYPED_TEST_SUITE_P( SpanTest