native 0.0.1
Vectors, masks and wide register packs for C++26
Loading...
Searching...
No Matches
packing_body.h
1// SPDX-FileCopyrightText: 2026 Edward Kmett <ekmett@gmail.com>
2// SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0
3
4
5namespace native {
6#if NATIVE_HAS_AVX2 || NATIVE_HAS_ARM_NEON || NATIVE_HAS_WASM_SIMD128
9 template <simd_integer_element To, simd_integer_element From, std::size_t N, ::native::isa<> Arch>
10 requires NATIVE_ARCH_REQUIRES(Arch) && (std::is_unsigned_v<To> && std::is_unsigned_v<From> &&
11 sizeof(From) == 2 * sizeof(To) &&
12 // The selected backend must implement this width. Complete storage alone
13 // does not establish the arithmetic instructions used by this operation.
14 (N * sizeof(From) == 16
15#if NATIVE_HAS_AVX2
16 || N * sizeof(From) == 32
17#endif
18#if NATIVE_HAS_AVX512F
19 || (N * sizeof(From) == 64 && (sizeof(From) >= 4 || NATIVE_HAS_AVX512BW != 0))
20#endif
21 ) &&
22 requires { sizeof(simd<From, N, Arch>); sizeof(simd<To, 2 * N, Arch>); })
25 using result = simd<To, 2 * N, Arch>;
26 if consteval {
27 std::array<From,N> first{},second{};
28 a.store(first.data()); b.store(second.data());
29 std::array<To,2*N> lanes{};
30 for(std::size_t i=0;i<N;++i) {
31 lanes[i]=static_cast<To>(first[i]);
32 lanes[N+i]=static_cast<To>(second[i]);
33 }
34 return result(lanes);
35 }
36#if NATIVE_HAS_WASM_SIMD128
37 return [&]<std::size_t... I>(std::index_sequence<I...>) {
38 using vector_type = To __attribute__((ext_vector_type(2 * N)));
39 auto first = __builtin_bit_cast(vector_type, a.to_native());
40 auto second = __builtin_bit_cast(vector_type, b.to_native());
41 return result::from_native(__builtin_bit_cast(
42 v128_t, __builtin_shufflevector(first, second, (2 * I)..., (2 * N + 2 * I)...)));
43 }(std::make_index_sequence<N>{});
44#elif NATIVE_HAS_ARM_NEON
45 if constexpr (sizeof(From) == 8)
46 return result::from_native(vreinterpretq_u8_u32(vcombine_u32(
47 vmovn_u64(vreinterpretq_u64_u8(a.to_native())),
48 vmovn_u64(vreinterpretq_u64_u8(b.to_native())))));
49 else if constexpr (sizeof(From) == 4)
50 return result::from_native(vreinterpretq_u8_u16(vcombine_u16(
51 vmovn_u32(vreinterpretq_u32_u8(a.to_native())),
52 vmovn_u32(vreinterpretq_u32_u8(b.to_native())))));
53 else return result::from_native(vcombine_u8(
54 vmovn_u16(vreinterpretq_u16_u8(a.to_native())),
55 vmovn_u16(vreinterpretq_u16_u8(b.to_native()))));
56#else
57 if constexpr (N * sizeof(From) == 16) {
58 if constexpr (sizeof(From) == 8)
59 return result::from_native(_mm_unpacklo_epi64(
60 _mm_shuffle_epi32(a.to_native(), _MM_SHUFFLE(2, 0, 2, 0)),
61 _mm_shuffle_epi32(b.to_native(), _MM_SHUFFLE(2, 0, 2, 0))));
62 else if constexpr (sizeof(From) == 4) {
63 auto mask = _mm_set1_epi32(0xffff);
64 return result::from_native(_mm_packus_epi32(
65 _mm_and_si128(a.to_native(), mask), _mm_and_si128(b.to_native(), mask)));
66 } else {
67 auto mask = _mm_set1_epi16(0xff);
68 return result::from_native(_mm_packus_epi16(
69 _mm_and_si128(a.to_native(), mask), _mm_and_si128(b.to_native(), mask)));
70 }
71 } else if constexpr (N * sizeof(From) == 32) {
72 if constexpr (sizeof(From) == 8) {
73 auto order = _mm256_setr_epi32(0, 2, 4, 6, 0, 2, 4, 6);
74 auto low = _mm256_castsi256_si128(_mm256_permutevar8x32_epi32(a.to_native(), order));
75 auto high = _mm256_castsi256_si128(_mm256_permutevar8x32_epi32(b.to_native(), order));
76 return result::from_native(_mm256_inserti128_si256(_mm256_castsi128_si256(low), high, 1));
77 } else if constexpr (sizeof(From) == 4) {
78 auto mask = _mm256_set1_epi32(0xffff);
79 auto packed = _mm256_packus_epi32(_mm256_and_si256(a.to_native(), mask),
80 _mm256_and_si256(b.to_native(), mask));
81 return result::from_native(_mm256_permute4x64_epi64(packed, _MM_SHUFFLE(3, 1, 2, 0)));
82 } else {
83 auto mask = _mm256_set1_epi16(0xff);
84 auto packed = _mm256_packus_epi16(_mm256_and_si256(a.to_native(), mask),
85 _mm256_and_si256(b.to_native(), mask));
86 return result::from_native(_mm256_permute4x64_epi64(packed, _MM_SHUFFLE(3, 1, 2, 0)));
87 }
88#if NATIVE_HAS_AVX512F
89 } else {
90 auto truncate = [](auto x) {
91 if constexpr (sizeof(From) == 8) return _mm512_cvtepi64_epi32(x);
92 else if constexpr (sizeof(From) == 4) return _mm512_cvtepi32_epi16(x);
93 else return _mm512_cvtepi16_epi8(x);
94 };
95 auto low = truncate(a.to_native()), high = truncate(b.to_native());
96 return result::from_native(_mm512_inserti64x4(_mm512_castsi256_si512(low), high, 1));
97#endif
98 }
99#endif
100 }
101#endif
102}
#define native_inline
inline [[always_inline]]
Definition attributes.h:212
#define native_nodiscard
C++17 [[nodiscard]].
Definition attributes.h:189
#define native_const
[[const]] is not const
Definition attributes.h:108
typename mask_traits< std::remove_cvref_t< T > >::type mask
Definition mask_traits.h:22
Architecture-tagged vectors, register packs and supporting value types. Native arithmetic follows its...
constexpr simd< To, 2 *N, Arch > narrow_concat(simd< From, N, Arch > a, simd< From, N, Arch > b) noexcept
Omitted architecture arguments use the native.simd provider's baseline.