native 0.0.1
Vectors, masks and wide register packs for C++26
Loading...
Searching...
No Matches
native.x86.avxneconvert.ccm
1// SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0
2module;
3#include "native/isa_import.h"
4#include "native/config.h"
5#include "native/attributes.h"
6#include <array>
7#include <bit>
8#include <cstdint>
9#include <immintrin.h>
10export module native.x86.avxneconvert;
11export import native.x86.features;
12export import native.simd;
13
14namespace native::detail::x86_neconvert {
15 template<class T> constexpr std::uint32_t widen(T value) noexcept {
16 if constexpr (std::same_as<T, bf16>)
17 return std::uint32_t(value.to_bits()) << 16;
18 else
19 return std::bit_cast<std::uint32_t>(static_cast<float>(value));
20 }
21 template<isa<x86> A, std::size_t N, unsigned Stride, unsigned Offset, class T>
22 consteval simd<float, N, A> widen_memory(T const * source) noexcept {
23 // Validate the complete instruction memory operand during constant evaluation.
24 if constexpr (Stride == 2) {
25 auto last = source[2 * N - 1].to_bits();
26 (void)last;
27 }
28 std::array<float, N> result{};
29 for (std::size_t i = 0; i < N; ++i)
30 result[i] = std::bit_cast<float>(widen(source[Stride * i + Offset]));
31 return simd<float, N, A>::load(result.data());
32 }
33 template<isa<x86> A, std::size_t N>
34 consteval simd<bf16, N, A> narrow(simd<float, N, A> input) noexcept {
35 std::array<float, N> values{};
36 input.store(values.data());
37 std::array<std::uint16_t, N> result{};
38 for (std::size_t i = 0; i < N; ++i) {
39 auto bits = std::bit_cast<std::uint32_t>(values[i]);
40 auto magnitude = bits & 0x7fffffffu;
41 if (magnitude < 0x00800000u)
42 result[i] = std::uint16_t((bits >> 16) & 0x8000u);
43 else if (magnitude > 0x7f800000u)
44 result[i] = std::uint16_t((bits >> 16) | 0x0040u);
45 else
46 result[i] = std::uint16_t((bits + 0x7fffu + ((bits >> 16) & 1u)) >> 16);
47 }
48 return simd<bf16, N, A>::load_bits(result.data());
49 }
50} // namespace native::detail::x86_neconvert
51
52export namespace native {
62
65 template<isa<x86> Arch, std::size_t N>
66 requires(Arch.has(x86_feature::avxneconvert) && Arch.has(x86_feature::avx) &&
67 (N == 4 || N == 8))
69 "avxneconvert") constexpr simd<float, N, Arch> bcstnebf16_ps(bf16 const * source) noexcept {
70 if consteval {
71 return detail::x86_neconvert::widen_memory<Arch, N, 0, 0>(source);
72 } else {
73 if constexpr (N == 4)
74 return simd<float, N, Arch>::from_native(_mm_bcstnebf16_ps(source));
75 else
76 return simd<float, N, Arch>::from_native(_mm256_bcstnebf16_ps(source));
77 }
78 }
79
81 template<isa<x86> Arch, std::size_t N>
82 requires(!(Arch.has(x86_feature::avxneconvert) && Arch.has(x86_feature::avx)) &&
83 (N == 4 || N == 8) && requires { sizeof(simd<float, N, Arch>); })
84 native_nodiscard consteval simd<float, N, Arch> bcstnebf16_ps(bf16 const * source) noexcept {
85 return detail::x86_neconvert::widen_memory<Arch, N, 0, 0>(source);
86 }
87
90 template<isa<x86> Arch, std::size_t N>
91 requires(Arch.has(x86_feature::avxneconvert) && Arch.has(x86_feature::avx) &&
92 (N == 4 || N == 8))
94 "avxneconvert") constexpr simd<float, N, Arch> bcstnesh_ps(fp16 const * source) noexcept {
95 if consteval {
96 return detail::x86_neconvert::widen_memory<Arch, N, 0, 0>(source);
97 } else {
98 if constexpr (N == 4)
99 return simd<float, N, Arch>::from_native(_mm_bcstnesh_ps(source));
100 else
101 return simd<float, N, Arch>::from_native(_mm256_bcstnesh_ps(source));
102 }
103 }
104
106 template<isa<x86> Arch, std::size_t N>
107 requires(!(Arch.has(x86_feature::avxneconvert) && Arch.has(x86_feature::avx)) &&
108 (N == 4 || N == 8) && requires { sizeof(simd<float, N, Arch>); })
109 native_nodiscard consteval simd<float, N, Arch> bcstnesh_ps(fp16 const * source) noexcept {
110 return detail::x86_neconvert::widen_memory<Arch, N, 0, 0>(source);
111 }
112
115 template<isa<x86> Arch, std::size_t N>
116 requires(Arch.has(x86_feature::avxneconvert) && Arch.has(x86_feature::avx) &&
117 (N == 4 || N == 8))
119 "avxneconvert") constexpr simd<float, N, Arch> cvtneebf16_ps(bf16 const * source) noexcept {
120 if consteval {
121 return detail::x86_neconvert::widen_memory<Arch, N, 2, 0>(source);
122 } else {
123 if constexpr (N == 4)
125 _mm_cvtneebf16_ps(reinterpret_cast<__m128bh const *>(source)));
126 else
128 _mm256_cvtneebf16_ps(reinterpret_cast<__m256bh const *>(source)));
129 }
130 }
131
133 template<isa<x86> Arch, std::size_t N>
134 requires(!(Arch.has(x86_feature::avxneconvert) && Arch.has(x86_feature::avx)) &&
135 (N == 4 || N == 8) && requires { sizeof(simd<float, N, Arch>); })
136 native_nodiscard consteval simd<float, N, Arch> cvtneebf16_ps(bf16 const * source) noexcept {
137 return detail::x86_neconvert::widen_memory<Arch, N, 2, 0>(source);
138 }
139
142 template<isa<x86> Arch, std::size_t N>
143 requires(Arch.has(x86_feature::avxneconvert) && Arch.has(x86_feature::avx) &&
144 (N == 4 || N == 8))
146 "avxneconvert") constexpr simd<float, N, Arch> cvtneeph_ps(fp16 const * source) noexcept {
147 if consteval {
148 return detail::x86_neconvert::widen_memory<Arch, N, 2, 0>(source);
149 } else {
150 if constexpr (N == 4)
152 _mm_cvtneeph_ps(reinterpret_cast<__m128h const *>(source)));
153 else
155 _mm256_cvtneeph_ps(reinterpret_cast<__m256h const *>(source)));
156 }
157 }
158
160 template<isa<x86> Arch, std::size_t N>
161 requires(!(Arch.has(x86_feature::avxneconvert) && Arch.has(x86_feature::avx)) &&
162 (N == 4 || N == 8) && requires { sizeof(simd<float, N, Arch>); })
163 native_nodiscard consteval simd<float, N, Arch> cvtneeph_ps(fp16 const * source) noexcept {
164 return detail::x86_neconvert::widen_memory<Arch, N, 2, 0>(source);
165 }
166
169 template<isa<x86> Arch, std::size_t N>
170 requires(Arch.has(x86_feature::avxneconvert) && Arch.has(x86_feature::avx) &&
171 (N == 4 || N == 8))
173 "avxneconvert") constexpr simd<float, N, Arch> cvtneobf16_ps(bf16 const * source) noexcept {
174 if consteval {
175 return detail::x86_neconvert::widen_memory<Arch, N, 2, 1>(source);
176 } else {
177 if constexpr (N == 4)
179 _mm_cvtneobf16_ps(reinterpret_cast<__m128bh const *>(source)));
180 else
182 _mm256_cvtneobf16_ps(reinterpret_cast<__m256bh const *>(source)));
183 }
184 }
185
187 template<isa<x86> Arch, std::size_t N>
188 requires(!(Arch.has(x86_feature::avxneconvert) && Arch.has(x86_feature::avx)) &&
189 (N == 4 || N == 8) && requires { sizeof(simd<float, N, Arch>); })
190 native_nodiscard consteval simd<float, N, Arch> cvtneobf16_ps(bf16 const * source) noexcept {
191 return detail::x86_neconvert::widen_memory<Arch, N, 2, 1>(source);
192 }
193
196 template<isa<x86> Arch, std::size_t N>
197 requires(Arch.has(x86_feature::avxneconvert) && Arch.has(x86_feature::avx) &&
198 (N == 4 || N == 8))
200 "avxneconvert") constexpr simd<float, N, Arch> cvtneoph_ps(fp16 const * source) noexcept {
201 if consteval {
202 return detail::x86_neconvert::widen_memory<Arch, N, 2, 1>(source);
203 } else {
204 if constexpr (N == 4)
206 _mm_cvtneoph_ps(reinterpret_cast<__m128h const *>(source)));
207 else
209 _mm256_cvtneoph_ps(reinterpret_cast<__m256h const *>(source)));
210 }
211 }
212
214 template<isa<x86> Arch, std::size_t N>
215 requires(!(Arch.has(x86_feature::avxneconvert) && Arch.has(x86_feature::avx)) &&
216 (N == 4 || N == 8) && requires { sizeof(simd<float, N, Arch>); })
217 native_nodiscard consteval simd<float, N, Arch> cvtneoph_ps(fp16 const * source) noexcept {
218 return detail::x86_neconvert::widen_memory<Arch, N, 2, 1>(source);
219 }
220
223 template<isa<x86> Arch, std::size_t N>
224 requires(Arch.has(x86_feature::avxneconvert) && Arch.has(x86_feature::avx) &&
225 (N == 4 || N == 8))
228 simd<float, N, Arch> input) noexcept {
229 if consteval {
230 return detail::x86_neconvert::narrow(input);
231 } else {
232 using R = simd<bf16, N, Arch>;
233 // Copy the complete register representation. An existing AVX512_BF16
234 // storage specialization may attach stronger targets to its named bridge.
235 if constexpr (N == 4)
236 return std::bit_cast<R>(_mm_cvtneps_avx_pbh(input.to_native()));
237 else
238 return std::bit_cast<R>(_mm256_cvtneps_avx_pbh(input.to_native()));
239 }
240 }
241
243 template<isa<x86> Arch, std::size_t N>
244 requires(!(Arch.has(x86_feature::avxneconvert) && Arch.has(x86_feature::avx)) &&
245 (N == 4 || N == 8) &&
246 requires {
247 sizeof(simd<float, N, Arch>);
248 sizeof(simd<bf16, N, Arch>);
249 })
251 return detail::x86_neconvert::narrow(input);
252 }
253
254} // namespace native
Compiler attributes for host code, with shader-safe shared modifiers.
#define native_inline
inline [[always_inline]]
Definition attributes.h:212
#define native_nodiscard
C++17 [[nodiscard]].
Definition attributes.h:189
#define native_target(x)
this indicates a required feature set for the current multiversioned function.
Definition attributes.h:476
constexpr simd< float, N, Arch > bcstnesh_ps(fp16 const *source) noexcept
constexpr simd< float, N, Arch > cvtneoph_ps(fp16 const *source) noexcept
constexpr simd< float, N, Arch > cvtneeph_ps(fp16 const *source) noexcept
constexpr simd< float, N, Arch > cvtneebf16_ps(bf16 const *source) noexcept
constexpr simd< bf16, N, Arch > cvtneps_bf16(simd< float, N, Arch > input) noexcept
constexpr simd< float, N, Arch > bcstnebf16_ps(bf16 const *source) noexcept
constexpr simd< float, N, Arch > cvtneobf16_ps(bf16 const *source) noexcept
Architecture-tagged vectors, register packs and supporting value types. Native arithmetic follows its...
Omitted architecture arguments use the native.simd provider's baseline.