From a19e10dacdba45a957a42635bef4a41a32417fe0 Mon Sep 17 00:00:00 2001 From: Denis Yaroshevskiy Date: Thu, 3 Oct 2024 11:07:07 -0700 Subject: [PATCH] folly::simd::contains, all types. (#2306) Summary: Pull Request resolved: https://github.com/facebook/folly/pull/2306 extended the SimdCharPlatform => SimdPlatform to support all unsigned types. Reviewed By: Gownta Differential Revision: D63548781 --- folly/algorithm/simd/detail/BUCK | 7 +- folly/algorithm/simd/detail/ContainsImpl.h | 20 +- .../algorithm/simd/detail/SimdCharPlatform.h | 267 ----------- folly/algorithm/simd/detail/SimdForEach.h | 10 +- folly/algorithm/simd/detail/SimdPlatform.h | 445 ++++++++++++++++++ folly/algorithm/simd/detail/test/BUCK | 3 +- .../simd/detail/test/SimdAnyOfTest.cpp | 29 +- folly/algorithm/simd/test/ContainsTest.cpp | 8 +- folly/detail/BUCK | 4 +- folly/detail/SimpleSimdStringUtils.cpp | 4 +- folly/detail/SimpleSimdStringUtilsImpl.h | 8 +- folly/detail/SplitStringSimd.cpp | 8 +- folly/detail/SplitStringSimdImpl.h | 32 +- folly/detail/test/BUCK | 2 +- .../detail/test/SimpleSimdStringUtilsTest.cpp | 14 +- folly/detail/test/SplitStringSimdTest.cpp | 15 +- folly/somerge_defs.bzl | 5 +- 17 files changed, 543 insertions(+), 338 deletions(-) delete mode 100644 folly/algorithm/simd/detail/SimdCharPlatform.h create mode 100644 folly/algorithm/simd/detail/SimdPlatform.h diff --git a/folly/algorithm/simd/detail/BUCK b/folly/algorithm/simd/detail/BUCK index d8db2c8f55f..3e65d7a1cd1 100644 --- a/folly/algorithm/simd/detail/BUCK +++ b/folly/algorithm/simd/detail/BUCK @@ -16,8 +16,8 @@ cpp_library( ) cpp_library( - name = "simd_char_platform", - headers = ["SimdCharPlatform.h"], + name = "simd_platform", + headers = ["SimdPlatform.h"], exported_deps = [ "//folly:portability", "//folly/algorithm/simd:ignore", @@ -31,7 +31,7 @@ cpp_library( headers = ["ContainsImpl.h"], exported_deps = [ ":simd_any_of", - ":simd_char_platform", + ":simd_platform", "//folly:c_portability", "//folly/container:span", ], @@ -45,6 +45,7 @@ cpp_library( "//folly:c_portability", "//folly:traits", "//folly/algorithm/simd:ignore", + "//folly/lang:align", ], ) diff --git a/folly/algorithm/simd/detail/ContainsImpl.h b/folly/algorithm/simd/detail/ContainsImpl.h index 66d049f4545..7e24a143caf 100644 --- a/folly/algorithm/simd/detail/ContainsImpl.h +++ b/folly/algorithm/simd/detail/ContainsImpl.h @@ -23,7 +23,7 @@ #include #include -#include +#include #include namespace folly::simd::detail { @@ -62,20 +62,20 @@ FOLLY_ERASE bool containsImplStd(folly::span haystack, T needle) { template constexpr bool hasHandwrittenContains() { - return std::is_same_v && - !std::is_same_v; + return !std::is_same_v, void> && + (std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v); } template FOLLY_ERASE bool containsImplHandwritten( folly::span haystack, T needle) { - static_assert(std::is_same_v, ""); - auto as_chars = folly::reinterpret_span_cast(haystack); - return simdAnyOf( - as_chars.data(), - as_chars.data() + as_chars.size(), - [&](SimdCharPlatform::reg_t x) { - return SimdCharPlatform::equal(x, static_cast(needle)); + static_assert(!std::is_same_v, void>, ""); + return simdAnyOf, 4>( + haystack.data(), + haystack.data() + haystack.size(), + [&](typename SimdPlatform::reg_t x) { + return SimdPlatform::equal(x, static_cast(needle)); }); } diff --git a/folly/algorithm/simd/detail/SimdCharPlatform.h b/folly/algorithm/simd/detail/SimdCharPlatform.h deleted file mode 100644 index 9d1254acf9d..00000000000 --- a/folly/algorithm/simd/detail/SimdCharPlatform.h +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include -#include - -#include - -#if FOLLY_X64 -#include -#endif - -#if FOLLY_AARCH64 -#include -#endif - -namespace folly { -namespace simd::detail { - -/** - * SimdCharPlatform - * - * Common interface for some SIMD operations on chars between: sse2, avx2, - * arm-neon. (maybe we will move to sse4.2 at some point, we don't care much for - * pure sse2). - * - * If it's not one of the supported platforms, std::same_as. - * There is also a macro: FOLLY_DETAIL_HAS_SIMD_CHAR_PLATFORM set to 1 or 0 - * - * Nested types: - * - reg_t - type of a simd register (__m128i) - * - logical_t - type of a simd logical register (matches reg_t so far) - * - * Nested constants: - * - kCardinal - number of elements in a register - * - * loads: - * - loadu(const char*, ignore_none) - * - unsafeLoadU(const char*, ignore_none) - * - loada(const char*, ignore) - * - * a/u stand for aligned/unaligned. Ignored values can be garbage. unsafe - * disables sanitizers. - * - * reg ops: - * - equal(reg_t, char) - by lane comparison against a char. - * - le_unsigned(reg_t, char) - by lane less than or equal to char. - * - * logical ops: - * - any(logical_t, ignore) - return true if any the lanes are true - * - logical_or(logical_t, logical_t) - by lane logical or - * - */ - -#if FOLLY_X64 || FOLLY_AARCH64 - -template -struct SimdCharPlatformCommon : Platform { - using logical_t = typename Platform::logical_t; - - // These are aligned loads but there is no point in generating - // aligned load instructions, so we call loadu. - FOLLY_ALWAYS_INLINE - static auto loada(const char* ptr, ignore_none) { - return Platform::loadu(ptr, ignore_none{}); - } - - FOLLY_ALWAYS_INLINE - static auto loada(const char* ptr, ignore_extrema) { - return Platform::unsafeLoadu(ptr, ignore_none{}); - } - - using Platform::any; - - FOLLY_ALWAYS_INLINE - static bool any(typename Platform::logical_t log, ignore_extrema ignore) { - std::pair mmask = movemask(log, ignore); - return mmask.first; - } - - static auto toArray(typename Platform::reg_t x) { - std::array buf; - std::memcpy(buf.data(), &x, Platform::kCardinal); - return buf; - } -}; - -#endif - -#if FOLLY_X64 - -struct SimdCharSse2PlatformSpecific { - using reg_t = __m128i; - using logical_t = reg_t; - - static constexpr int kCardinal = 16; - - // Even for aligned loads intel people don't recommend using - // aligned load instruction - FOLLY_ALWAYS_INLINE - static reg_t loadu(const char* p, ignore_none) { - return _mm_loadu_si128(reinterpret_cast(p)); - } - - FOLLY_DISABLE_SANITIZERS - FOLLY_ALWAYS_INLINE - static reg_t unsafeLoadu(const char* p, ignore_none) { - return _mm_loadu_si128(reinterpret_cast(p)); - } - - FOLLY_ALWAYS_INLINE - static logical_t equal(reg_t reg, char x) { - return _mm_cmpeq_epi8(reg, _mm_set1_epi8(x)); - } - - FOLLY_ALWAYS_INLINE - static logical_t le_unsigned(reg_t reg, char x) { - // No unsigned comparisons on x86 - // less equal <=> equal (min) - reg_t min = _mm_min_epu8(reg, _mm_set1_epi8(x)); - return _mm_cmpeq_epi8(reg, min); - } - - FOLLY_ALWAYS_INLINE - static logical_t logical_or(logical_t x, logical_t y) { - return _mm_or_si128(x, y); - } - - FOLLY_ALWAYS_INLINE - static bool any(logical_t log, ignore_none) { - return movemask(log).first; - } -}; - -#define FOLLY_DETAIL_HAS_SIMD_CHAR_PLATFORM 1 - -using SimdCharSse2Platform = - SimdCharPlatformCommon; - -#if defined(__AVX2__) - -struct SimdCharAvx2PlatformSpecific { - using reg_t = __m256i; - using logical_t = reg_t; - - static constexpr int kCardinal = 32; - - // We can actually use aligned loads but our Intel people don't recommend - FOLLY_ALWAYS_INLINE - static reg_t loadu(const char* p, ignore_none) { - return _mm256_loadu_si256(reinterpret_cast(p)); - } - - FOLLY_DISABLE_SANITIZERS - FOLLY_ALWAYS_INLINE - static reg_t unsafeLoadu(const char* p, ignore_none) { - return _mm256_loadu_si256(reinterpret_cast(p)); - } - - FOLLY_ALWAYS_INLINE - static logical_t equal(reg_t reg, char x) { - return _mm256_cmpeq_epi8(reg, _mm256_set1_epi8(x)); - } - - FOLLY_ALWAYS_INLINE - static logical_t le_unsigned(reg_t reg, char x) { - // See SSE comment - reg_t min = _mm256_min_epu8(reg, _mm256_set1_epi8(x)); - return _mm256_cmpeq_epi8(reg, min); - } - - FOLLY_ALWAYS_INLINE - static logical_t logical_or(logical_t x, logical_t y) { - return _mm256_or_si256(x, y); - } - - FOLLY_ALWAYS_INLINE - static bool any(logical_t log, ignore_none) { - return simd::movemask(log).first; - } -}; - -using SimdCharAvx2Platform = - SimdCharPlatformCommon; - -using SimdCharPlatform = SimdCharAvx2Platform; - -#else -using SimdCharPlatform = SimdCharSse2Platform; -#endif - -#elif FOLLY_AARCH64 - -struct SimdCharAarch64PlatformSpecific { - using reg_t = uint8x16_t; - using logical_t = reg_t; - - static constexpr int kCardinal = 16; - - FOLLY_ALWAYS_INLINE - static reg_t loadu(const char* p, ignore_none) { - return vld1q_u8(reinterpret_cast(p)); - } - - FOLLY_DISABLE_SANITIZERS - FOLLY_ALWAYS_INLINE - static reg_t unsafeLoadu(const char* p, ignore_none) { - return vld1q_u8(reinterpret_cast(p)); - } - - FOLLY_ALWAYS_INLINE - static logical_t equal(reg_t reg, char x) { - return vceqq_u8(reg, vdupq_n_u8(static_cast(x))); - } - - FOLLY_ALWAYS_INLINE - static logical_t le_unsigned(reg_t reg, char x) { - return vcleq_u8(reg, vdupq_n_u8(static_cast(x))); - } - - FOLLY_ALWAYS_INLINE - static logical_t logical_or(logical_t x, logical_t y) { - return vorrq_u8(x, y); - } - - FOLLY_ALWAYS_INLINE - static bool any(logical_t log, ignore_none) { return vmaxvq_u8(log); } -}; - -#define FOLLY_DETAIL_HAS_SIMD_CHAR_PLATFORM 1 - -using SimdCharAarch64Platform = - SimdCharPlatformCommon; - -using SimdCharPlatform = SimdCharAarch64Platform; - -#define FOLLY_DETAIL_HAS_SIMD_CHAR_PLATFORM 1 - -#else - -#define FOLLY_DETAIL_HAS_SIMD_CHAR_PLATFORM 0 - -using SimdCharPlatform = void; - -#endif - -} // namespace simd::detail -} // namespace folly diff --git a/folly/algorithm/simd/detail/SimdForEach.h b/folly/algorithm/simd/detail/SimdForEach.h index 71f80ea4c71..3dd8b42dbd8 100644 --- a/folly/algorithm/simd/detail/SimdForEach.h +++ b/folly/algorithm/simd/detail/SimdForEach.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -67,15 +68,12 @@ FOLLY_ALWAYS_INLINE void simdForEachAligning( /** * previousAlignedAddress * - * Given a pointer returns a closest pointer aligned to a given size. - * (it just masks out some lower bits) + * Given a pointer returns a closest pointer aligned to a given size + * (in elements). */ template FOLLY_ALWAYS_INLINE T* previousAlignedAddress(T* ptr, int to) { - std::uintptr_t uptr = reinterpret_cast(ptr); - std::uintptr_t uto = static_cast(to); - uptr &= ~(uto - 1); - return reinterpret_cast(uptr); + return align_floor(ptr, sizeof(T) * to); } /** diff --git a/folly/algorithm/simd/detail/SimdPlatform.h b/folly/algorithm/simd/detail/SimdPlatform.h new file mode 100644 index 00000000000..172a14b9e76 --- /dev/null +++ b/folly/algorithm/simd/detail/SimdPlatform.h @@ -0,0 +1,445 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +#if FOLLY_SSE_PREREQ(4, 2) +#include +#endif + +#if FOLLY_AARCH64 +#include +#endif + +namespace folly { +namespace simd::detail { + +/** + * SimdPlatform + * + * Common interface for some SIMD operations between: sse4.2, avx2, + * arm-neon. + * + * Supported types for T at the moment are uint8_16/uint16_t/uint32_t/uint64_t + * + * If it's not one of the supported platforms: + * std::same_as, void> + * There is also a macro: FOLLY_DETAIL_HAS_SIMD_PLATFORM set to 1 or 0 + * + **/ + +#if FOLLY_SSE_PREREQ(4, 2) || FOLLY_AARCH64 + +template +struct SimdPlatformCommon { + /** + * sclar_t - type of scalar we operate on (uint8_t, uint16_t etc) + * reg_t - type of a simd register (__m128i) + * logical_t - type of a simd logical register (matches reg_t so far) + **/ + using scalar_t = typename Platform::scalar_t; + using reg_t = typename Platform::reg_t; + using logical_t = typename Platform::logical_t; + + static constexpr int kCardinal = sizeof(reg_t) / sizeof(scalar_t); + + /** + * loads: + * precondition: at least one element should be not ignored. + * + * loada - load from an aligned (to sizeof(reg_t)) address + * loadu - load from an unaligned address + * unsafeLoadu - load from an unaligned address that disables sanitizers. + * This is for reading a register within a page + * but maybe outside of the array's boundary. + * + * Ignored values can be garbage. + **/ + template + static reg_t loada(const scalar_t* ptr, Ignore); + static reg_t loadu(const scalar_t* ptr, ignore_none); + static reg_t unsafeLoadu(const scalar_t* ptr, ignore_none); + + /** + * Comparing reg_t against the scalar. + * + * NOTE: less_equal only implemented for uint8_t + * for now. + **/ + static logical_t equal(reg_t reg, scalar_t x); + static logical_t less_equal(reg_t reg, scalar_t x); + + /** + * logical reduction + **/ + template + static bool any(logical_t logical, Ignore ignore); + + /** + * logical operations + **/ + static logical_t logical_or(logical_t x, logical_t y); + + /** + * Converting register to an array for debugging + **/ + static auto toArray(reg_t x); +}; + +template +template +FOLLY_ERASE auto SimdPlatformCommon::loada( + const scalar_t* ptr, Ignore) -> reg_t { + if constexpr (std::is_same_v) { + // There is not point to aligned load instructions + // on modern cpus. Arm doesn't even have any. + return loadu(ptr, ignore_none{}); + } else { + // We have a precondition: at least one element is loaded. + // From this we can prove that we can unsafely load from + // and aligned address. + // + // Here is an explanation from Stephen Canon: + // https://stackoverflow.com/questions/25566302/vectorized-strlen-getting-away-with-reading-unallocated-memory + return unsafeLoadu(ptr, ignore_none{}); + } +} + +template +FOLLY_ERASE auto SimdPlatformCommon::loadu( + const scalar_t* ptr, ignore_none) -> reg_t { + return Platform::loadu(ptr); +} + +template +FOLLY_ERASE auto SimdPlatformCommon::unsafeLoadu( + const scalar_t* ptr, ignore_none) -> reg_t { + return Platform::unsafeLoadu(ptr); +} + +template +FOLLY_ERASE auto SimdPlatformCommon::equal(reg_t reg, scalar_t x) + -> logical_t { + return Platform::equal(reg, Platform::broadcast(x)); +} + +template +FOLLY_ERASE auto SimdPlatformCommon::less_equal(reg_t reg, scalar_t x) + -> logical_t { + static_assert(std::is_same_v, "not implemented"); + return Platform::less_equal(reg, Platform::broadcast(x)); +} + +template +template +FOLLY_ERASE bool SimdPlatformCommon::any( + logical_t logical, Ignore ignore) { + if constexpr (std::is_same_v) { + return Platform::any(logical); + } else { + return movemask(logical, ignore).first; + } +} + +template +FOLLY_ERASE auto SimdPlatformCommon::logical_or( + logical_t x, logical_t y) -> logical_t { + return Platform::logical_or(x, y); +} + +template +FOLLY_ERASE auto SimdPlatformCommon::toArray(reg_t x) { + std::array res; + std::memcpy(&res, &x, sizeof(x)); + return res; +} + +#endif + +#if FOLLY_SSE_PREREQ(4, 2) + +template +struct SimdSse2PlatformSpecific { + using scalar_t = T; + using reg_t = __m128i; + using logical_t = reg_t; + + FOLLY_ERASE + static reg_t loadu(const scalar_t* p) { + return _mm_loadu_si128(reinterpret_cast(p)); + } + + FOLLY_DISABLE_SANITIZERS + FOLLY_ERASE + static reg_t unsafeLoadu(const scalar_t* p) { + return _mm_loadu_si128(reinterpret_cast(p)); + } + + FOLLY_ERASE + static reg_t broadcast(scalar_t x) { + if constexpr (std::is_same_v) { + return _mm_set1_epi8(x); + } else if constexpr (std::is_same_v) { + return _mm_set1_epi16(x); + } else if constexpr (std::is_same_v) { + return _mm_set1_epi32(x); + } else if constexpr (std::is_same_v) { + return _mm_set1_epi64x(x); + } + } + + FOLLY_ERASE + static logical_t equal(reg_t x, reg_t y) { + if constexpr (std::is_same_v) { + return _mm_cmpeq_epi8(x, y); + } else if constexpr (std::is_same_v) { + return _mm_cmpeq_epi16(x, y); + } else if constexpr (std::is_same_v) { + return _mm_cmpeq_epi32(x, y); + } else if constexpr (std::is_same_v) { + return _mm_cmpeq_epi64(x, y); + } + } + + FOLLY_ERASE + static logical_t less_equal(reg_t x, reg_t y) { + static_assert( + std::is_same_v, "other types not implemented"); + // No unsigned comparisons on x86 + // less equal <=> equal (min) + reg_t min = _mm_min_epu8(x, y); + return equal(x, min); + } + + FOLLY_ERASE + static logical_t logical_or(logical_t x, logical_t y) { + return _mm_or_si128(x, y); + } + + FOLLY_ERASE + static bool any(logical_t log) { return movemask(log).first; } +}; + +#define FOLLY_DETAIL_HAS_SIMD_PLATFORM 1 + +template +using SimdSse2Platform = SimdPlatformCommon>; + +#if defined(__AVX2__) + +template +struct SimdAvx2PlatformSpecific { + using scalar_t = T; + using reg_t = __m256i; + using logical_t = reg_t; + + FOLLY_ERASE + static reg_t loadu(const scalar_t* p) { + return _mm256_loadu_si256(reinterpret_cast(p)); + } + + FOLLY_DISABLE_SANITIZERS + FOLLY_ERASE + static reg_t unsafeLoadu(const scalar_t* p) { + return _mm256_loadu_si256(reinterpret_cast(p)); + } + + FOLLY_ERASE + static reg_t broadcast(scalar_t x) { + if constexpr (std::is_same_v) { + return _mm256_set1_epi8(x); + } else if constexpr (std::is_same_v) { + return _mm256_set1_epi16(x); + } else if constexpr (std::is_same_v) { + return _mm256_set1_epi32(x); + } else if constexpr (std::is_same_v) { + return _mm256_set1_epi64x(x); + } + } + + FOLLY_ERASE + static logical_t equal(reg_t x, reg_t y) { + if constexpr (std::is_same_v) { + return _mm256_cmpeq_epi8(x, y); + } else if constexpr (std::is_same_v) { + return _mm256_cmpeq_epi16(x, y); + } else if constexpr (std::is_same_v) { + return _mm256_cmpeq_epi32(x, y); + } else if constexpr (std::is_same_v) { + return _mm256_cmpeq_epi64(x, y); + } + } + + FOLLY_ERASE + static logical_t less_equal(reg_t x, reg_t y) { + static_assert( + std::is_same_v, "other types not implemented"); + // See SSE comment + reg_t min = _mm256_min_epu8(x, y); + return _mm256_cmpeq_epi8(x, min); + } + + FOLLY_ERASE + static logical_t logical_or(logical_t x, logical_t y) { + return _mm256_or_si256(x, y); + } + + FOLLY_ERASE + static bool any(logical_t log) { + return simd::movemask(log).first; + } +}; + +template +using SimdAvx2Platform = SimdPlatformCommon>; + +template +using SimdPlatform = SimdAvx2Platform; + +#else + +template +using SimdPlatform = SimdPlatformCommon>; + +#endif + +#elif FOLLY_AARCH64 + +template +struct SimdAarch64PlatformSpecific { + using scalar_t = T; + + FOLLY_ERASE + static auto loadu(const scalar_t* p) { + if constexpr (std::is_same_v) { + return vld1q_u8(p); + } else if constexpr (std::is_same_v) { + return vld1q_u16(p); + } else if constexpr (std::is_same_v) { + return vld1q_u32(p); + } else if constexpr (std::is_same_v) { + return vld1q_u64(p); + } + } + + using reg_t = decltype(loadu(nullptr)); + using logical_t = reg_t; + + FOLLY_DISABLE_SANITIZERS + FOLLY_ERASE + static reg_t unsafeLoadu(const scalar_t* p) { + if constexpr (std::is_same_v) { + return vld1q_u8(p); + } else if constexpr (std::is_same_v) { + return vld1q_u16(p); + } else if constexpr (std::is_same_v) { + return vld1q_u32(p); + } else if constexpr (std::is_same_v) { + return vld1q_u64(p); + } + } + + FOLLY_ERASE + static reg_t broadcast(scalar_t x) { + if constexpr (std::is_same_v) { + return vdupq_n_u8(x); + } else if constexpr (std::is_same_v) { + return vdupq_n_u16(x); + } else if constexpr (std::is_same_v) { + return vdupq_n_u32(x); + } else if constexpr (std::is_same_v) { + return vdupq_n_u64(x); + } + } + + FOLLY_ERASE + static logical_t equal(reg_t x, reg_t y) { + if constexpr (std::is_same_v) { + return vceqq_u8(x, y); + } else if constexpr (std::is_same_v) { + return vceqq_u16(x, y); + } else if constexpr (std::is_same_v) { + return vceqq_u32(x, y); + } else if constexpr (std::is_same_v) { + return vceqq_u64(x, y); + } + } + + FOLLY_ERASE + static logical_t less_equal(reg_t x, reg_t y) { + if constexpr (std::is_same_v) { + return vcleq_u8(x, y); + } else if constexpr (std::is_same_v) { + return vcleq_u16(x, y); + } else if constexpr (std::is_same_v) { + return vcleq_u32(x, y); + } else if constexpr (std::is_same_v) { + return vcleq_u64(x, y); + } + } + + FOLLY_ALWAYS_INLINE + static logical_t logical_or(logical_t x, logical_t y) { + if constexpr (std::is_same_v) { + return vorrq_u8(x, y); + } else if constexpr (std::is_same_v) { + return vorrq_u16(x, y); + } else if constexpr (std::is_same_v) { + return vorrq_u32(x, y); + } else if constexpr (std::is_same_v) { + return vorrq_u64(x, y); + } + } + + FOLLY_ALWAYS_INLINE + static bool any(logical_t log) { + // https://github.com/dotnet/runtime/pull/75864 + auto u32 = bit_cast(log); + u32 = vpmaxq_u32(u32, u32); + auto u64 = bit_cast(u32); + return vgetq_lane_u64(u64, 0); + } +}; + +#define FOLLY_DETAIL_HAS_SIMD_PLATFORM 1 + +template +using SimdAarch64Platform = SimdPlatformCommon>; + +template +using SimdPlatform = SimdAarch64Platform; + +#define FOLLY_DETAIL_HAS_SIMD_PLATFORM 1 + +#else + +#define FOLLY_DETAIL_HAS_SIMD_PLATFORM 0 + +template +using SimdPlatform = void; + +#endif + +} // namespace simd::detail +} // namespace folly diff --git a/folly/algorithm/simd/detail/test/BUCK b/folly/algorithm/simd/detail/test/BUCK index dea91d505cf..3e17cae5947 100644 --- a/folly/algorithm/simd/detail/test/BUCK +++ b/folly/algorithm/simd/detail/test/BUCK @@ -10,7 +10,8 @@ cpp_unittest( deps = [ "//folly:range", "//folly/algorithm/simd/detail:simd_any_of", - "//folly/algorithm/simd/detail:simd_char_platform", + "//folly/algorithm/simd/detail:simd_platform", + "//folly/container:span", "//folly/portability:gtest", ], ) diff --git a/folly/algorithm/simd/detail/test/SimdAnyOfTest.cpp b/folly/algorithm/simd/detail/test/SimdAnyOfTest.cpp index 8ea006f0eae..39e9ce2ff34 100644 --- a/folly/algorithm/simd/detail/test/SimdAnyOfTest.cpp +++ b/folly/algorithm/simd/detail/test/SimdAnyOfTest.cpp @@ -17,27 +17,31 @@ #include #include -#include +#include +#include #include #include -#if FOLLY_DETAIL_HAS_SIMD_CHAR_PLATFORM +#if FOLLY_DETAIL_HAS_SIMD_PLATFORM namespace folly { namespace simd::detail { template -void anySpacesTestForPlatformUnrolling(folly::StringPiece s, bool expected) { +void anySpacesTestForPlatformUnrolling( + folly::span s, bool expected) { bool actual = simdAnyOf( s.data(), s.data() + s.size(), [](typename Platform::reg_t x) { return Platform::equal(x, ' '); }); - ASSERT_EQ(expected, actual) << s; + ASSERT_EQ(expected, actual) + << folly::StringPiece(folly::reinterpret_span_cast(s)); } template -void anySpacesTestForPlatform(folly::StringPiece s, bool expected) { +void anySpacesTestForPlatform( + folly::span s, bool expected) { ASSERT_NO_FATAL_FAILURE( (anySpacesTestForPlatformUnrolling(s, expected))); ASSERT_NO_FATAL_FAILURE( @@ -48,20 +52,23 @@ void anySpacesTestForPlatform(folly::StringPiece s, bool expected) { (anySpacesTestForPlatformUnrolling(s, expected))); } -void anySpacesTest(folly::StringPiece s, bool expected) { +void anySpacesTest(folly::StringPiece sChars, bool expected) { + auto s = + folly::reinterpret_span_cast(folly::span(sChars)); + ASSERT_NO_FATAL_FAILURE( - anySpacesTestForPlatform(s, expected)); + anySpacesTestForPlatform>(s, expected)); #if FOLLY_X64 ASSERT_NO_FATAL_FAILURE( - anySpacesTestForPlatform(s, expected)); + anySpacesTestForPlatform>(s, expected)); #if defined(__AVX2__) ASSERT_NO_FATAL_FAILURE( - anySpacesTestForPlatform(s, expected)); + anySpacesTestForPlatform>(s, expected)); #endif #endif #if FOLLY_AARCH64 ASSERT_NO_FATAL_FAILURE( - anySpacesTestForPlatform(s, expected)); + anySpacesTestForPlatform>(s, expected)); #endif } @@ -114,4 +121,4 @@ TEST(SimdAnyOfSimple, BigChunk) { } // namespace simd::detail } // namespace folly -#endif // FOLLY_DETAIL_HAS_SIMD_CHAR_PLATFORM +#endif // FOLLY_DETAIL_HAS_SIMD_PLATFORM diff --git a/folly/algorithm/simd/test/ContainsTest.cpp b/folly/algorithm/simd/test/ContainsTest.cpp index 0b628b4ee76..55f1966d458 100644 --- a/folly/algorithm/simd/test/ContainsTest.cpp +++ b/folly/algorithm/simd/test/ContainsTest.cpp @@ -64,7 +64,7 @@ void testSimdContainsVerify(folly::span haystack, T needle, bool expected) { ASSERT_EQ(expected, actual2) << " haystack.size(): " << haystack.size(); } - if constexpr (std::is_same_v) { + if constexpr (simd::detail::hasHandwrittenContains()) { bool actual3 = simd::detail::containsImplHandwritten(const_haystack, needle); ASSERT_EQ(expected, actual3) << " haystack.size(): " << haystack.size(); @@ -80,11 +80,13 @@ TYPED_TEST(ContainsTest, Basic) { ++offset) { folly::span haystack(buf.data() + offset, buf.data() + buf.size()); T needle{1}; - testSimdContainsVerify(haystack, needle, /*expected*/ false); + ASSERT_NO_FATAL_FAILURE( + testSimdContainsVerify(haystack, needle, /*expected*/ false)); for (auto& x : haystack) { x = needle; - testSimdContainsVerify(haystack, needle, /*expected*/ true); + ASSERT_NO_FATAL_FAILURE( + testSimdContainsVerify(haystack, needle, /*expected*/ true)); x = 0; } } diff --git a/folly/detail/BUCK b/folly/detail/BUCK index 121a9959f89..7f76b46a1f4 100644 --- a/folly/detail/BUCK +++ b/folly/detail/BUCK @@ -250,7 +250,7 @@ cpp_library( exported_deps = [ "//folly:range", "//folly/algorithm/simd/detail:simd_any_of", - "//folly/algorithm/simd/detail:simd_char_platform", + "//folly/algorithm/simd/detail:simd_platform", ], ) @@ -271,8 +271,8 @@ cpp_library( "//folly:range", "//folly/algorithm/simd:ignore", "//folly/algorithm/simd:movemask", - "//folly/algorithm/simd/detail:simd_char_platform", "//folly/algorithm/simd/detail:simd_for_each", + "//folly/algorithm/simd/detail:simd_platform", "//folly/lang:bits", ], ) diff --git a/folly/detail/SimpleSimdStringUtils.cpp b/folly/detail/SimpleSimdStringUtils.cpp index f426ebc1e90..8ef8deeff61 100644 --- a/folly/detail/SimpleSimdStringUtils.cpp +++ b/folly/detail/SimpleSimdStringUtils.cpp @@ -16,7 +16,7 @@ #include -#include +#include #include namespace folly { @@ -24,7 +24,7 @@ namespace detail { bool simdHasSpaceOrCntrlSymbols(folly::StringPiece s) { return SimpleSimdStringUtilsImpl< - simd::detail::SimdCharPlatform>::hasSpaceOrCntrlSymbols(s); + simd::detail::SimdPlatform>::hasSpaceOrCntrlSymbols(s); } } // namespace detail diff --git a/folly/detail/SimpleSimdStringUtilsImpl.h b/folly/detail/SimpleSimdStringUtilsImpl.h index d0e2e73efe8..8a99a9a323c 100644 --- a/folly/detail/SimpleSimdStringUtilsImpl.h +++ b/folly/detail/SimpleSimdStringUtilsImpl.h @@ -18,7 +18,7 @@ #include #include -#include +#include namespace folly { namespace detail { @@ -35,14 +35,16 @@ struct SimpleSimdStringUtilsImpl { logical_t operator()(reg_t reg) { // This happens to be equivalent to std::isspace(c) || std::iscntrl(c) return Platform::logical_or( - Platform::le_unsigned(reg, 0x20), Platform::equal(reg, 0x7F)); + Platform::less_equal(reg, 0x20), Platform::equal(reg, 0x7F)); } }; FOLLY_ALWAYS_INLINE static bool hasSpaceOrCntrlSymbols(folly::StringPiece s) { return simd::detail::simdAnyOf( - s.data(), s.data() + s.size(), HasSpaceOrCntrlSymbolsLambda{}); + reinterpret_cast(s.data()), + reinterpret_cast(s.data() + s.size()), + HasSpaceOrCntrlSymbolsLambda{}); } }; diff --git a/folly/detail/SplitStringSimd.cpp b/folly/detail/SplitStringSimd.cpp index b658f120593..e868a2f9638 100644 --- a/folly/detail/SplitStringSimd.cpp +++ b/folly/detail/SplitStringSimd.cpp @@ -28,7 +28,7 @@ template void SimdSplitByCharImpl::keepEmpty( char sep, folly::StringPiece what, Container& res) { PlatformSimdSplitByChar< - simd::detail::SimdCharPlatform, + simd::detail::SimdPlatform, /*ignoreEmpty*/ false>{}(sep, what, res); } @@ -36,7 +36,7 @@ template void SimdSplitByCharImpl::dropEmpty( char sep, folly::StringPiece what, Container& res) { PlatformSimdSplitByChar< - simd::detail::SimdCharPlatform, + simd::detail::SimdPlatform, /*ignoreEmpty*/ true>{}(sep, what, res); } @@ -44,7 +44,7 @@ template void SimdSplitByCharImplToStrings::keepEmpty( char sep, folly::StringPiece what, Container& res) { PlatformSimdSplitByChar< - simd::detail::SimdCharPlatform, + simd::detail::SimdPlatform, /*ignoreEmpty*/ false>{}(sep, what, res); } @@ -52,7 +52,7 @@ template void SimdSplitByCharImplToStrings::dropEmpty( char sep, folly::StringPiece what, Container& res) { PlatformSimdSplitByChar< - simd::detail::SimdCharPlatform, + simd::detail::SimdPlatform, /*ignoreEmpty*/ true>{}(sep, what, res); } diff --git a/folly/detail/SplitStringSimdImpl.h b/folly/detail/SplitStringSimdImpl.h index 2120527ebe2..34b26b6c247 100644 --- a/folly/detail/SplitStringSimdImpl.h +++ b/folly/detail/SplitStringSimdImpl.h @@ -20,8 +20,8 @@ #include #include #include -#include #include +#include #include #if FOLLY_X64 @@ -73,18 +73,18 @@ struct PlatformSimdSplitByChar { template FOLLY_ALWAYS_INLINE void emplaceBack( - Container& res, const char* f, const char* l) const { + Container& res, const std::uint8_t* f, const std::uint8_t* l) const { if (ignoreEmpty && f == l) { return; } - res.emplace_back(f, l - f); + res.emplace_back(reinterpret_cast(f), l - f); } template FOLLY_ALWAYS_INLINE void outputStringsFoMmask( std::pair mmask, - const char* pos, - const char*& prev, + const std::uint8_t* pos, + const std::uint8_t*& prev, Container& res) const { // reserve was not beneficial on benchmarks. Uint mmaskBits = mmask.first; @@ -94,7 +94,7 @@ struct PlatformSimdSplitByChar { mmaskBits >>= BitsPerElement{}; auto firstSet = counted / BitsPerElement{}; - const char* split = pos + firstSet; + const std::uint8_t* split = pos + firstSet; pos = split + 1; emplaceBack(res, prev, split); prev = pos; @@ -104,13 +104,13 @@ struct PlatformSimdSplitByChar { template struct ForEachDelegate { const PlatformSimdSplitByChar& self; - char sep; - const char*& prev; + std::uint8_t sep; + const std::uint8_t*& prev; Container& res; template FOLLY_ALWAYS_INLINE bool step( - const char* ptr, Ignore ignore, UnrollIndex) const { + const std::uint8_t* ptr, Ignore ignore, UnrollIndex) const { reg_t loaded = Platform::loada(ptr, ignore); auto mmask = simd::movemask(Platform::equal(loaded, sep), ignore); @@ -122,11 +122,17 @@ struct PlatformSimdSplitByChar { template FOLLY_ALWAYS_INLINE void operator()( char sep, folly::StringPiece what, Container& res) const { - const char* prev = what.data(); - ForEachDelegate delegate{*this, sep, prev, res}; + const std::uint8_t* what_f = + reinterpret_cast(what.data()); + const std::uint8_t* what_l = what_f + what.size(); + + const std::uint8_t* prev = what_f; + + ForEachDelegate delegate{ + *this, static_cast(sep), prev, res}; simd::detail::simdForEachAligning( - Platform::kCardinal, what.data(), what.data() + what.size(), delegate); - emplaceBack(res, prev, what.data() + what.size()); + Platform::kCardinal, what_f, what_l, delegate); + emplaceBack(res, prev, what_l); } }; diff --git a/folly/detail/test/BUCK b/folly/detail/test/BUCK index 72581223d59..ba9ce3152bb 100644 --- a/folly/detail/test/BUCK +++ b/folly/detail/test/BUCK @@ -64,7 +64,7 @@ cpp_unittest( "SimpleSimdStringUtilsTest.cpp", ], deps = [ - "//folly/algorithm/simd/detail:simd_char_platform", + "//folly/algorithm/simd/detail:simd_platform", "//folly/detail:simple_simd_string_utils", "//folly/portability:gtest", ], diff --git a/folly/detail/test/SimpleSimdStringUtilsTest.cpp b/folly/detail/test/SimpleSimdStringUtilsTest.cpp index cd3ba233738..fcb974cc7c4 100644 --- a/folly/detail/test/SimpleSimdStringUtilsTest.cpp +++ b/folly/detail/test/SimpleSimdStringUtilsTest.cpp @@ -16,7 +16,7 @@ #include -#include +#include #include #include @@ -36,14 +36,20 @@ void testHasSpaceOrCntrlSymbols(folly::StringPiece s, bool r) { ASSERT_EQ(r, hasSpaceOrCntrlSymbolsForPlatform(s)) << s; #if FOLLY_X64 - ASSERT_EQ(r, hasSpaceOrCntrlSymbolsForPlatform(s)) << s; + ASSERT_EQ( + r, hasSpaceOrCntrlSymbolsForPlatform>(s)) + << s; #if defined(__AVX2__) - ASSERT_EQ(r, hasSpaceOrCntrlSymbolsForPlatform(s)) << s; + ASSERT_EQ( + r, hasSpaceOrCntrlSymbolsForPlatform>(s)) + << s; #endif #endif #if FOLLY_AARCH64 - ASSERT_EQ(r, hasSpaceOrCntrlSymbolsForPlatform(s)) + ASSERT_EQ( + r, + hasSpaceOrCntrlSymbolsForPlatform>(s)) << s; #endif } diff --git a/folly/detail/test/SplitStringSimdTest.cpp b/folly/detail/test/SplitStringSimdTest.cpp index ec0c6009754..21139cb9fbc 100644 --- a/folly/detail/test/SplitStringSimdTest.cpp +++ b/folly/detail/test/SplitStringSimdTest.cpp @@ -146,19 +146,22 @@ void runTestStringSplitOneType(folly::StringPiece s) { #if FOLLY_X64 actuals.emplace_back(); - PlatformSimdSplitByChar{}( - ',', s, actuals.back()); + PlatformSimdSplitByChar< + simd::detail::SimdSse2Platform, + ignoreEmpty>{}(',', s, actuals.back()); #if defined(__AVX2__) actuals.emplace_back(); - PlatformSimdSplitByChar{}( - ',', s, actuals.back()); + PlatformSimdSplitByChar< + simd::detail::SimdAvx2Platform, + ignoreEmpty>{}(',', s, actuals.back()); #endif #endif #if FOLLY_AARCH64 actuals.emplace_back(); - PlatformSimdSplitByChar{}( - ',', s, actuals.back()); + PlatformSimdSplitByChar< + simd::detail::SimdAarch64Platform, + ignoreEmpty>{}(',', s, actuals.back()); #endif for (const auto& actual : actuals) { diff --git a/folly/somerge_defs.bzl b/folly/somerge_defs.bzl index 9eb682e706a..1a1204e6110 100644 --- a/folly/somerge_defs.bzl +++ b/folly/somerge_defs.bzl @@ -2,7 +2,7 @@ Generated by xplat/cross_plat_devx/somerge_maps/compute_merge_maps.py -@generated SignedSource<> +@generated SignedSource<> """ # Entry Points: @@ -92,9 +92,10 @@ FOLLY_NATIVE_LIBRARY_MERGE_MAP = [ # //xplat/folly:uri FOLLY_BASE_NATIVE_LIBRARY_MERGE_MAP = [ "fbsource//xplat/folly/algorithm/simd/detail:simd_any_ofAndroid", - "fbsource//xplat/folly/algorithm/simd/detail:simd_char_platformAndroid", "fbsource//xplat/folly/algorithm/simd/detail:simd_for_eachAndroid", + "fbsource//xplat/folly/algorithm/simd/detail:simd_platformAndroid", "fbsource//xplat/folly/algorithm/simd/detail:unroll_utilsAndroid", + "fbsource//xplat/folly/algorithm/simd:ignoreAndroid", "fbsource//xplat/folly/algorithm/simd:movemaskAndroid", "fbsource//xplat/folly/chrono:hardwareAndroid", "fbsource//xplat/folly/compression:compressionAndroid",