native 0.0.1
Vectors, masks and wide register packs for C++26
Loading...
Searching...
No Matches
native.simd.ccm
1// SPDX-FileCopyrightText: 2026 Edward Kmett <ekmett@gmail.com>
2// SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0
3module;
4#include "native/isa_import.h"
5#include <native/simd.h>
6#include <native/integer.h>
7#include <native/packing.h>
8#include <native/simd/math/bits.h>
9#include <native/targets.h>
10export module native.simd;
11export import native.numerics;
12export import native.wide;
13export import native.static_string;
14export import native.types;
15export import native.memory;
16// Defaults use the owning module provider's baseline.
17extern "C++" namespace native {
19 template<class T, std::size_t N, isa<> Arch = NATIVE_BASELINE> struct simd;
20}
21// Keep the GMF partial specializations reachable to module consumers.
22export namespace native { using ::native::mask_traits; }
23#include "native/simd/exports.h"
24#if NATIVE_HOST_X86
25#define NATIVE_ARCH_REQUIRES(A) (::native::avx512_bf16 <= A)
26#pragma clang attribute push(__attribute__((target("avx2,fma,avx512f,avx512dq,avx512bw,avx512vl,avx512bf16"))), apply_to=function)
27export namespace native {
28 namespace detail {
29 template<std::size_t N,::native::isa<> A> requires NATIVE_ARCH_REQUIRES(A) &&(N==8 || N==16 || N==32)
30 struct value_traits<simd<bf16,N,A>> {
31 static constexpr isa<> value=avx512_bf16;
32 static constexpr bool known=true;
33 static constexpr bool aggregate_default=false;
34 };
35 }
43 template<std::size_t N, ::native::isa<> Arch> requires NATIVE_ARCH_REQUIRES(Arch) &&(N == 8 || N == 16 || N == 32)
48 static constexpr isa<> architecture=Arch;
52 using native_type = std::conditional_t<N == 8,__m128bh,std::conditional_t<N == 16,__m256bh,__m512bh>>;
58 using mask_type = mask;
65 template<class T> using rebind = simd<T,N,architecture>;
67 static constexpr std::size_t lanes = N;
68 private:
69 native_type value_;
70 public:
72 simd() noexcept = default;
74 native_inline constexpr explicit simd(bf16 value) noexcept
75 : value_(__builtin_bit_cast(native_type,bits_type(value.to_bits()).to_native())) {}
76
77 native_inline constexpr explicit simd(std::array<bf16,lanes> const & values) noexcept : simd(load(values.data())) {}
79 template<class... T> requires(sizeof...(T) == lanes && (std::same_as<T,bf16> && ...))
80 native_inline constexpr simd(T... values) noexcept : simd(std::array<bf16,lanes>{values...}) {}
82 native_inline constexpr simd(native_type value) noexcept : value_(value) {}
84 native_nodiscard native_inline constexpr operator native_type() const noexcept { return value_; }
85 // Native interoperability must not add elementwise BF16 arithmetic.
86#include "native/simd/bf16_reject_operators.h"
88 native_nodiscard native_inline constexpr native_type to_native() const noexcept { return value_; }
90 native_nodiscard static native_inline constexpr simd from_native(native_type value) noexcept {
91 simd result; result.value_ = value; return result;
92 }
93
94 native_nodiscard native_inline constexpr bits_type bits() const noexcept {
95 return bits_type::from_native(__builtin_bit_cast(typename bits_type::native_type,value_));
96 }
97
98 native_nodiscard native_inline constexpr bits_type to_bits() const noexcept { return bits(); }
100 native_nodiscard static native_inline constexpr simd from_bits(bits_type value) noexcept {
101 return from_native(__builtin_bit_cast(native_type,value.to_native()));
102 }
103
105 native_nodiscard static native_inline constexpr simd load_bits(std::uint16_t const * p) noexcept {
106 return from_bits(bits_type::load(p));
107 }
108
110 native_inline constexpr void store_bits(std::uint16_t * p) const noexcept { bits().store(p); }
115 template<std::size_t Alignment = 1>
116 native_nodiscard static native_inline constexpr simd load_memory(bf16 const * p) noexcept {
117 static_assert(Alignment > 0 && (Alignment & (Alignment - 1)) == 0);
118 if consteval {
119 std::array<std::uint16_t,lanes> words{};
120 for (std::size_t i=0;i<lanes;++i) words[i]=p[i].to_bits();
121 return load_bits(words.data());
122 }
123 native_type value; std::memcpy(&value, p, sizeof(value)); return from_native(value);
124 }
125
129 template<std::size_t Alignment = 1>
130 native_inline constexpr void store_memory(bf16 * p) const noexcept {
131 static_assert(Alignment > 0 && (Alignment & (Alignment - 1)) == 0);
132 if consteval {
133 std::array<std::uint16_t,lanes> words{}; store_bits(words.data());
134 for (std::size_t i=0;i<lanes;++i) p[i]=bf16::from_bits(words[i]);
135 return;
136 }
137 std::memcpy(p, &value_, sizeof(value_));
138 }
139
140 native_nodiscard static native_inline constexpr simd load(bf16 const * p) noexcept { return load_memory(p); }
142 native_inline constexpr void store(bf16 * p) const noexcept { store_memory(p); }
144 native_nodiscard static native_inline constexpr simd loadu(bf16 const * p) noexcept { return load(p); }
146 native_inline constexpr void storeu(bf16 * p) const noexcept { store(p); }
151 native_nodiscard static native_inline constexpr simd load_partial(bf16 const * p, std::size_t n,
152 bf16 fill = bf16::from_bits(0)) noexcept native_diagnose_if(n > simd::lanes,"partial SIMD count exceeds the lane count") {
153 assert(n <= lanes);
154 std::array<bf16,lanes> values; values.fill(fill);
155 if consteval {
156 for (std::size_t i=0;i<n;++i) values[i]=p[i];
157 } else { if (n) std::memcpy(values.data(), p, n * sizeof(bf16)); }
158 return load(values.data());
159 }
160
163 native_inline constexpr void store_partial(bf16 * p, std::size_t n) const noexcept native_diagnose_if(n > simd::lanes,"partial SIMD count exceeds the lane count") {
164 assert(n <= lanes);
165 if consteval {
166 std::array<std::uint16_t,lanes> words{}; store_bits(words.data());
167 for (std::size_t i=0;i<n;++i) p[i]=bf16::from_bits(words[i]);
168 } else { if (n) std::memcpy(p, &value_, n * sizeof(bf16)); }
169 }
170 };
171
172
179 template<std::size_t N, ::native::isa<> Arch> requires NATIVE_ARCH_REQUIRES(Arch) &&(N == 8 || N == 16 || N == 32)
182 simd<float,N/2,Arch> accumulator) noexcept {
183 if consteval { return detail::half_constant::dot2<false>(a,b,accumulator); }
184 return simd<float,N/2,Arch>::from_native(
185 detail::avx512_bf16_backend::dot2_native(a.to_native(), b.to_native(), accumulator.to_native()));
186 }
187}
188
189#pragma clang attribute pop
190#undef NATIVE_ARCH_REQUIRES
191#define NATIVE_ARCH_REQUIRES(A) (::native::avx512_fp16 <= A)
192#pragma clang attribute push(__attribute__((target("avx2,fma,avx512f,avx512dq,avx512bw,avx512vl,avx512fp16"))), apply_to=function)
193export namespace native {
194 namespace detail {
195 template<::native::isa<> A> requires NATIVE_ARCH_REQUIRES(A)
196 struct value_traits<simd<fp16,32,A>> {
197 static constexpr isa<> value=avx512_fp16;
198 static constexpr bool known=true;
199 static constexpr bool aggregate_default=false;
200 };
201 }
214 template<::native::isa<> Arch> requires NATIVE_ARCH_REQUIRES(Arch) struct simd<fp16,32,Arch> {
218 static constexpr isa<> architecture=Arch;
222 using native_type = __m512h;
235 template<class T> using rebind = simd<T,32,architecture>;
237 static constexpr std::size_t lanes = 32;
238 private:
239 native_type value_;
240 public:
242 simd() noexcept = default;
244 native_inline constexpr explicit simd(fp16 value) noexcept
245 : value_(__builtin_bit_cast(native_type,bits_type(value.to_bits()).to_native())) {}
246
247 native_inline constexpr explicit simd(std::array<fp16,lanes> const & values) noexcept : simd(load(values.data())) {}
249 template<class... T> requires(sizeof...(T) == lanes && (std::same_as<T,fp16> && ...))
250 native_inline constexpr simd(T... values) noexcept : simd(std::array<fp16,lanes>{values...}) {}
252 native_inline constexpr simd(native_type value) noexcept : value_(value) {}
254 native_nodiscard native_inline constexpr operator native_type() const noexcept { return value_; }
256 native_nodiscard native_inline constexpr native_type to_native() const noexcept { return value_; }
258 native_nodiscard static native_inline constexpr simd from_native(native_type value) noexcept {
259 simd result; result.value_ = value; return result;
260 }
261
262 native_nodiscard native_inline constexpr bits_type bits() const noexcept {
263 return bits_type::from_native(__builtin_bit_cast(typename bits_type::native_type,value_));
264 }
265
266 native_nodiscard native_inline constexpr bits_type to_bits() const noexcept { return bits(); }
268 native_nodiscard static native_inline constexpr simd from_bits(bits_type value) noexcept {
269 return from_native(__builtin_bit_cast(native_type,value.to_native()));
270 }
271
273 native_nodiscard static native_inline constexpr simd load_bits(std::uint16_t const * p) noexcept {
274 return from_bits(bits_type::load(p));
275 }
276
278 native_inline constexpr void store_bits(std::uint16_t * p) const noexcept { bits().store(p); }
283 template<std::size_t Alignment = 1>
284 native_nodiscard static native_inline constexpr simd load_memory(fp16 const * p) noexcept {
285 static_assert(Alignment > 0 && (Alignment & (Alignment - 1)) == 0);
286 if consteval {
287 std::array<std::uint16_t,lanes> words{};
288 for (std::size_t i=0;i<lanes;++i) words[i]=p[i].to_bits();
289 return load_bits(words.data());
290 }
291 native_type value; std::memcpy(&value, p, sizeof(value)); return from_native(value);
292 }
293
297 template<std::size_t Alignment = 1>
298 native_inline constexpr void store_memory(fp16 * p) const noexcept {
299 static_assert(Alignment > 0 && (Alignment & (Alignment - 1)) == 0);
300 if consteval {
301 std::array<std::uint16_t,lanes> words{}; store_bits(words.data());
302 for (std::size_t i=0;i<lanes;++i) p[i]=fp16::from_bits(words[i]);
303 return;
304 }
305 std::memcpy(p, &value_, sizeof(value_));
306 }
307
308 native_nodiscard static native_inline constexpr simd load(fp16 const * p) noexcept { return load_memory(p); }
310 native_inline constexpr void store(fp16 * p) const noexcept { store_memory(p); }
312 native_nodiscard static native_inline constexpr simd loadu(fp16 const * p) noexcept { return load(p); }
314 native_inline constexpr void storeu(fp16 * p) const noexcept { store(p); }
319 native_nodiscard static native_inline constexpr simd load_partial(fp16 const * p, std::size_t n,
320 fp16 fill = fp16::from_bits(0)) noexcept native_diagnose_if(n > simd::lanes,"partial SIMD count exceeds the lane count") {
321 assert(n <= lanes);
322 std::array<fp16,lanes> values; values.fill(fill);
323 if consteval {
324 for (std::size_t i=0;i<n;++i) values[i]=p[i];
325 } else { if (n) std::memcpy(values.data(), p, n * sizeof(fp16)); }
326 return load(values.data());
327 }
328
331 native_inline constexpr void store_partial(fp16 * p, std::size_t n) const noexcept native_diagnose_if(n > simd::lanes,"partial SIMD count exceeds the lane count") {
332 assert(n <= lanes);
333 if consteval {
334 std::array<std::uint16_t,lanes> words{}; store_bits(words.data());
335 for (std::size_t i=0;i<n;++i) p[i]=fp16::from_bits(words[i]);
336 } else { if (n) std::memcpy(p, &value_, n * sizeof(fp16)); }
337 }
338
339 native_nodiscard friend native_inline constexpr simd operator+(simd a,simd b) noexcept {
340 if consteval { return detail::half_constant::arithmetic<detail::half_constant::operation::add,false>(a,b); }
341 return from_native(detail::avx512_fp16_backend::add_half(a.value_,b.value_));
342 }
343
344 native_nodiscard friend native_inline constexpr simd operator-(simd a,simd b) noexcept {
345 if consteval { return detail::half_constant::arithmetic<detail::half_constant::operation::subtract,false>(a,b); }
346 return from_native(detail::avx512_fp16_backend::sub_half(a.value_,b.value_));
347 }
348
349 native_nodiscard friend native_inline constexpr simd operator*(simd a,simd b) noexcept {
350 if consteval { return detail::half_constant::arithmetic<detail::half_constant::operation::multiply,false>(a,b); }
351 return from_native(detail::avx512_fp16_backend::mul_half(a.value_,b.value_));
352 }
353
355 native_nodiscard friend native_inline constexpr simd operator/(simd a,simd b) noexcept {
356 if consteval { return detail::half_constant::arithmetic<detail::half_constant::operation::divide,false>(a,b); }
357 return from_native(detail::avx512_fp16_backend::div_half(a.value_,b.value_));
358 }
359
362 native_nodiscard friend native_inline constexpr simd sqrt(simd a) noexcept {
363 if consteval { return detail::half_constant::square_root<false>(a); }
364 return from_native(detail::avx512_fp16_backend::sqrt_half(a.value_));
365 }
366
367 native_nodiscard friend native_inline constexpr simd operator-(simd a) noexcept {
368 if consteval { return detail::half_constant::unary(a,[](auto x) { return std::uint16_t(x^0x8000); }); }
369 return from_native(detail::avx512_fp16_backend::neg_half(a.value_));
370 }
371
373 native_nodiscard friend native_inline constexpr mask operator==(simd a,simd b) noexcept {
374 if consteval { return detail::half_constant::compare(a,b,[](auto x,auto y) { return detail::constexpr_float::equal_bits<detail::constexpr_float::binary16>(x,y); }); }
375 return mask::from_native(detail::avx512_fp16_backend::eq_half(a.value_,b.value_));
376 }
377
378 native_nodiscard friend native_inline constexpr mask operator!=(simd a,simd b) noexcept { return ~(a==b); }
380 native_nodiscard friend native_inline constexpr mask operator<(simd a,simd b) noexcept {
381 if consteval { return detail::half_constant::compare(a,b,[](auto x,auto y) { return detail::constexpr_float::less_bits<detail::constexpr_float::binary16>(x,y); }); }
382 return mask::from_native(detail::avx512_fp16_backend::lt_half(a.value_,b.value_));
383 }
384
385 native_nodiscard friend native_inline constexpr mask operator<=(simd a,simd b) noexcept {
386 if consteval { return detail::half_constant::compare(a,b,[](auto x,auto y) { return detail::constexpr_float::less_bits<detail::constexpr_float::binary16>(x,y) || detail::constexpr_float::equal_bits<detail::constexpr_float::binary16>(x,y); }); }
387 return mask::from_native(detail::avx512_fp16_backend::le_half(a.value_,b.value_));
388 }
389
390 native_nodiscard friend native_inline constexpr mask operator>(simd a,simd b) noexcept { return b<a; }
392 native_nodiscard friend native_inline constexpr mask operator>=(simd a,simd b) noexcept { return b<=a; }
395 native_nodiscard friend native_inline constexpr simd select(mask m,simd a,simd b) noexcept {
396 if consteval { return detail::half_constant::select(m,a,b); }
397 return from_native(detail::avx512_fp16_backend::select_half(m.to_native(),a.value_,b.value_));
398 }
399 };
400
401
405 template<::native::isa<> Arch> requires NATIVE_ARCH_REQUIRES(Arch)
407 simd<fp16,32,Arch> a,simd<fp16,32,Arch> b,simd<fp16,32,Arch> c) noexcept {
408 if consteval { return detail::half_constant::fused<false>(a,b,c); }
410 detail::avx512_fp16_backend::fma_half(a.to_native(),b.to_native(),c.to_native()));
411 }
412}
413
414#pragma clang attribute pop
415#undef NATIVE_ARCH_REQUIRES
416#endif
417#if NATIVE_HOST_NEON || defined(NATIVE_DOXYGEN)
418#define NATIVE_ARCH_REQUIRES(A) (::native::neon_bf16 <= A)
419#pragma clang attribute push(__attribute__((target("neon,bf16"))), apply_to=function)
420export namespace native {
421 namespace detail {
422 template<::native::isa<> A> requires NATIVE_ARCH_REQUIRES(A)
423 struct value_traits<simd<bf16,8,A>> {
424 static constexpr isa<> value=neon_bf16;
425 static constexpr bool known=true;
426 static constexpr bool aggregate_default=false;
427 };
428 }
436 template<::native::isa<> Arch> requires NATIVE_ARCH_REQUIRES(Arch) struct simd<bf16,8,Arch> {
440 static constexpr isa<> architecture=Arch;
444 using native_type = bfloat16x8_t;
457 template<class T> using rebind = simd<T,8,architecture>;
459 static constexpr std::size_t lanes = 8;
460 private:
461 native_type value_;
462 public:
464 simd() noexcept = default;
466 native_inline constexpr explicit simd(bf16 value) noexcept
467 : value_(__builtin_bit_cast(native_type,bits_type(value.to_bits()).to_native())) {}
468
469 native_inline constexpr explicit simd(std::array<bf16,lanes> const & values) noexcept : simd(load(values.data())) {}
471 template<class... T> requires(sizeof...(T) == lanes && (std::same_as<T,bf16> && ...))
472 native_inline constexpr simd(T... values) noexcept : simd(std::array<bf16,lanes>{values...}) {}
474 native_inline constexpr simd(native_type value) noexcept : value_(value) {}
476 native_nodiscard native_inline constexpr operator native_type() const noexcept { return value_; }
477 // Native interoperability must not add elementwise BF16 arithmetic.
478#include "native/simd/bf16_reject_operators.h"
480 native_nodiscard native_inline constexpr native_type to_native() const noexcept { return value_; }
482 native_nodiscard static native_inline constexpr simd from_native(native_type value) noexcept {
483 simd result; result.value_ = value; return result;
484 }
485
486 native_nodiscard native_inline constexpr bits_type bits() const noexcept {
487 return bits_type::from_native(__builtin_bit_cast(typename bits_type::native_type,value_));
488 }
489
490 native_nodiscard native_inline constexpr bits_type to_bits() const noexcept { return bits(); }
492 native_nodiscard static native_inline constexpr simd from_bits(bits_type value) noexcept {
493 return from_native(__builtin_bit_cast(native_type,value.to_native()));
494 }
495
497 native_nodiscard static native_inline constexpr simd load_bits(std::uint16_t const * p) noexcept {
498 return from_bits(bits_type::load(p));
499 }
500
502 native_inline constexpr void store_bits(std::uint16_t * p) const noexcept { bits().store(p); }
507 template<std::size_t Alignment = 1>
508 native_nodiscard static native_inline constexpr simd load_memory(bf16 const * p) noexcept {
509 static_assert(Alignment > 0 && (Alignment & (Alignment - 1)) == 0);
510 if consteval {
511 std::array<std::uint16_t,lanes> words{};
512 for (std::size_t i=0;i<lanes;++i) words[i]=p[i].to_bits();
513 return load_bits(words.data());
514 }
515 native_type value; std::memcpy(&value, p, sizeof(value)); return from_native(value);
516 }
517
521 template<std::size_t Alignment = 1>
522 native_inline constexpr void store_memory(bf16 * p) const noexcept {
523 static_assert(Alignment > 0 && (Alignment & (Alignment - 1)) == 0);
524 if consteval {
525 std::array<std::uint16_t,lanes> words{}; store_bits(words.data());
526 for (std::size_t i=0;i<lanes;++i) p[i]=bf16::from_bits(words[i]);
527 return;
528 }
529 std::memcpy(p, &value_, sizeof(value_));
530 }
531
532 native_nodiscard static native_inline constexpr simd load(bf16 const * p) noexcept { return load_memory(p); }
534 native_inline constexpr void store(bf16 * p) const noexcept { store_memory(p); }
536 native_nodiscard static native_inline constexpr simd loadu(bf16 const * p) noexcept { return load(p); }
538 native_inline constexpr void storeu(bf16 * p) const noexcept { store(p); }
543 native_nodiscard static native_inline constexpr simd load_partial(bf16 const * p, std::size_t n,
544 bf16 fill = bf16::from_bits(0)) noexcept native_diagnose_if(n > simd::lanes,"partial SIMD count exceeds the lane count") {
545 assert(n <= lanes);
546 std::array<bf16,lanes> values; values.fill(fill);
547 if consteval {
548 for (std::size_t i=0;i<n;++i) values[i]=p[i];
549 } else { if (n) std::memcpy(values.data(), p, n * sizeof(bf16)); }
550 return load(values.data());
551 }
552
555 native_inline constexpr void store_partial(bf16 * p, std::size_t n) const noexcept native_diagnose_if(n > simd::lanes,"partial SIMD count exceeds the lane count") {
556 assert(n <= lanes);
557 if consteval {
558 std::array<std::uint16_t,lanes> words{}; store_bits(words.data());
559 for (std::size_t i=0;i<n;++i) p[i]=bf16::from_bits(words[i]);
560 } else { if (n) std::memcpy(p, &value_, n * sizeof(bf16)); }
561 }
562 };
563
564
576 template<::native::isa<> Arch> requires NATIVE_ARCH_REQUIRES(Arch)
577 native_nodiscard native_inline constexpr simd<float,4,Arch> dot2(
578 simd<bf16,8,Arch> a, simd<bf16,8,Arch> b,
579 simd<float,4,Arch> accumulator) noexcept {
580 if consteval { return detail::half_constant::dot2<true>(a,b,accumulator); }
582 detail::neon_bf16_backend::dot2_native(a.to_native(), b.to_native(), accumulator.to_native()));
583 }
584}
585
586#pragma clang attribute pop
587#undef NATIVE_ARCH_REQUIRES
588#define NATIVE_ARCH_REQUIRES(A) (::native::neon_fp16 <= A)
589#pragma clang attribute push(__attribute__((target("neon,fullfp16"))), apply_to=function)
590export namespace native {
591 namespace detail {
592 template<::native::isa<> A> requires NATIVE_ARCH_REQUIRES(A)
593 struct value_traits<simd<fp16,8,A>> {
594 static constexpr isa<> value=neon_fp16;
595 static constexpr bool known=true;
596 static constexpr bool aggregate_default=false;
597 };
598 }
611 template<::native::isa<> Arch> requires NATIVE_ARCH_REQUIRES(Arch) struct simd<fp16,8,Arch> {
615 static constexpr isa<> architecture=Arch;
619 using native_type = float16x8_t;
632 template<class T> using rebind = simd<T,8,architecture>;
634 static constexpr std::size_t lanes = 8;
635 private:
636 native_type value_;
637 public:
639 simd() noexcept = default;
641 native_inline constexpr explicit simd(fp16 value) noexcept
642 : value_(__builtin_bit_cast(native_type,bits_type(value.to_bits()).to_native())) {}
643
644 native_inline constexpr explicit simd(std::array<fp16,lanes> const & values) noexcept : simd(load(values.data())) {}
646 template<class... T> requires(sizeof...(T) == lanes && (std::same_as<T,fp16> && ...))
647 native_inline constexpr simd(T... values) noexcept : simd(std::array<fp16,lanes>{values...}) {}
649 native_inline constexpr simd(native_type value) noexcept : value_(value) {}
651 native_nodiscard native_inline constexpr operator native_type() const noexcept { return value_; }
653 native_nodiscard native_inline constexpr native_type to_native() const noexcept { return value_; }
655 native_nodiscard static native_inline constexpr simd from_native(native_type value) noexcept {
656 simd result; result.value_ = value; return result;
657 }
658
659 native_nodiscard native_inline constexpr bits_type bits() const noexcept {
660 return bits_type::from_native(__builtin_bit_cast(typename bits_type::native_type,value_));
661 }
662
663 native_nodiscard native_inline constexpr bits_type to_bits() const noexcept { return bits(); }
665 native_nodiscard static native_inline constexpr simd from_bits(bits_type value) noexcept {
666 return from_native(__builtin_bit_cast(native_type,value.to_native()));
667 }
668
670 native_nodiscard static native_inline constexpr simd load_bits(std::uint16_t const * p) noexcept {
671 return from_bits(bits_type::load(p));
672 }
673
675 native_inline constexpr void store_bits(std::uint16_t * p) const noexcept { bits().store(p); }
680 template<std::size_t Alignment = 1>
681 native_nodiscard static native_inline constexpr simd load_memory(fp16 const * p) noexcept {
682 static_assert(Alignment > 0 && (Alignment & (Alignment - 1)) == 0);
683 if consteval {
684 std::array<std::uint16_t,lanes> words{};
685 for (std::size_t i=0;i<lanes;++i) words[i]=p[i].to_bits();
686 return load_bits(words.data());
687 }
688 native_type value; std::memcpy(&value, p, sizeof(value)); return from_native(value);
689 }
690
694 template<std::size_t Alignment = 1>
695 native_inline constexpr void store_memory(fp16 * p) const noexcept {
696 static_assert(Alignment > 0 && (Alignment & (Alignment - 1)) == 0);
697 if consteval {
698 std::array<std::uint16_t,lanes> words{}; store_bits(words.data());
699 for (std::size_t i=0;i<lanes;++i) p[i]=fp16::from_bits(words[i]);
700 return;
701 }
702 std::memcpy(p, &value_, sizeof(value_));
703 }
704
705 native_nodiscard static native_inline constexpr simd load(fp16 const * p) noexcept { return load_memory(p); }
707 native_inline constexpr void store(fp16 * p) const noexcept { store_memory(p); }
709 native_nodiscard static native_inline constexpr simd loadu(fp16 const * p) noexcept { return load(p); }
711 native_inline constexpr void storeu(fp16 * p) const noexcept { store(p); }
716 native_nodiscard static native_inline constexpr simd load_partial(fp16 const * p, std::size_t n,
717 fp16 fill = fp16::from_bits(0)) noexcept native_diagnose_if(n > simd::lanes,"partial SIMD count exceeds the lane count") {
718 assert(n <= lanes);
719 std::array<fp16,lanes> values; values.fill(fill);
720 if consteval {
721 for (std::size_t i=0;i<n;++i) values[i]=p[i];
722 } else { if (n) std::memcpy(values.data(), p, n * sizeof(fp16)); }
723 return load(values.data());
724 }
725
728 native_inline constexpr void store_partial(fp16 * p, std::size_t n) const noexcept native_diagnose_if(n > simd::lanes,"partial SIMD count exceeds the lane count") {
729 assert(n <= lanes);
730 if consteval {
731 std::array<std::uint16_t,lanes> words{}; store_bits(words.data());
732 for (std::size_t i=0;i<n;++i) p[i]=fp16::from_bits(words[i]);
733 } else { if (n) std::memcpy(p, &value_, n * sizeof(fp16)); }
734 }
735
736 native_nodiscard friend native_inline constexpr simd operator+(simd a,simd b) noexcept {
737 if consteval { return detail::half_constant::arithmetic<detail::half_constant::operation::add,true>(a,b); }
738 return from_native(detail::neon_fp16_backend::add_half(a.value_,b.value_));
739 }
740
741 native_nodiscard friend native_inline constexpr simd operator-(simd a,simd b) noexcept {
742 if consteval { return detail::half_constant::arithmetic<detail::half_constant::operation::subtract,true>(a,b); }
743 return from_native(detail::neon_fp16_backend::sub_half(a.value_,b.value_));
744 }
745
746 native_nodiscard friend native_inline constexpr simd operator*(simd a,simd b) noexcept {
747 if consteval { return detail::half_constant::arithmetic<detail::half_constant::operation::multiply,true>(a,b); }
748 return from_native(detail::neon_fp16_backend::mul_half(a.value_,b.value_));
749 }
750
752 native_nodiscard friend native_inline constexpr simd operator/(simd a,simd b) noexcept {
753 if consteval { return detail::half_constant::arithmetic<detail::half_constant::operation::divide,true>(a,b); }
754 return from_native(detail::neon_fp16_backend::div_half(a.value_,b.value_));
755 }
756
760 native_nodiscard friend native_inline constexpr simd sqrt(simd a) noexcept {
761 if consteval { return detail::half_constant::square_root<true>(a); }
762 return from_native(detail::neon_fp16_backend::sqrt_half(a.value_));
763 }
764
765 native_nodiscard friend native_inline constexpr simd operator-(simd a) noexcept {
766 if consteval { return detail::half_constant::unary(a,[](auto x) { return std::uint16_t(x^0x8000); }); }
767 return from_native(detail::neon_fp16_backend::neg_half(a.value_));
768 }
769
771 native_nodiscard friend native_inline constexpr mask operator==(simd a,simd b) noexcept {
772 if consteval { return detail::half_constant::compare(a,b,[](auto x,auto y) { return detail::constexpr_float::equal_bits<detail::constexpr_float::binary16>(x,y); }); }
773 return mask::unsafe_from_native(detail::neon_fp16_backend::eq_half(a.value_,b.value_));
774 }
775
776 native_nodiscard friend native_inline constexpr mask operator!=(simd a,simd b) noexcept { return ~(a==b); }
778 native_nodiscard friend native_inline constexpr mask operator<(simd a,simd b) noexcept {
779 if consteval { return detail::half_constant::compare(a,b,[](auto x,auto y) { return detail::constexpr_float::less_bits<detail::constexpr_float::binary16>(x,y); }); }
780 return mask::unsafe_from_native(detail::neon_fp16_backend::lt_half(a.value_,b.value_));
781 }
782
783 native_nodiscard friend native_inline constexpr mask operator<=(simd a,simd b) noexcept {
784 if consteval { return detail::half_constant::compare(a,b,[](auto x,auto y) { return detail::constexpr_float::less_bits<detail::constexpr_float::binary16>(x,y) || detail::constexpr_float::equal_bits<detail::constexpr_float::binary16>(x,y); }); }
785 return mask::unsafe_from_native(detail::neon_fp16_backend::le_half(a.value_,b.value_));
786 }
787
788 native_nodiscard friend native_inline constexpr mask operator>(simd a,simd b) noexcept { return b<a; }
790 native_nodiscard friend native_inline constexpr mask operator>=(simd a,simd b) noexcept { return b<=a; }
793 native_nodiscard friend native_inline constexpr simd select(mask m,simd a,simd b) noexcept {
794 if consteval { return detail::half_constant::select(m,a,b); }
795 return from_native(detail::neon_fp16_backend::select_half(m.to_native(),a.value_,b.value_));
796 }
797 };
798
799
803 template<::native::isa<> Arch> requires NATIVE_ARCH_REQUIRES(Arch)
805 simd<fp16,8,Arch> a,simd<fp16,8,Arch> b,simd<fp16,8,Arch> c) noexcept {
806 if consteval { return detail::half_constant::fused<true>(a,b,c); }
808 detail::neon_fp16_backend::fma_half(a.to_native(),b.to_native(),c.to_native()));
809 }
810}
811
812#pragma clang attribute pop
813#undef NATIVE_ARCH_REQUIRES
814#endif
815// SPDX-FileCopyrightText: 2026 Edward Kmett <ekmett@gmail.com>
816// SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0
817// Register shapes needed by instruction interfaces outside the arithmetic kernels.
818#if !NATIVE_HOST_WASM
819export namespace native {
820 namespace detail {
821 template<class T> concept instruction_element = simd_integer_element<T> ||
822 std::same_as<T,float> || std::same_as<T,double> ||
823 std::same_as<T,fp16> || std::same_as<T,bf16>;
824
825 template<class T, std::size_t N, isa<> A>
826 inline constexpr bool instruction_storage_shape = [] {
827 if constexpr(!instruction_element<T> || N<2) return false;
828 else if constexpr(N>64/sizeof(T)) return false;
829 else {
830 constexpr auto bytes=sizeof(T)*N;
831#if NATIVE_HOST_NEON
832 if constexpr(!(neon<=A)) return false;
833 if constexpr(std::same_as<T,fp16>) return N==4 || (N==8 && !(neon_fp16<=A));
834 if constexpr(std::same_as<T,bf16>) return N==4 || (N==8 && !(neon_bf16<=A));
835 if constexpr(std::same_as<T,double>) return N==2;
836 // Existing two-lane 32-bit vectors have intentional four-lane storage.
837 return simd_integer_element<T> && sizeof(T)<4 && bytes==8;
838#elif NATIVE_HOST_X86
839 if constexpr(!A.has(x86_feature::sse2)) return false;
840 if constexpr(std::same_as<T,fp16>) return N==4 || N==8;
841 if constexpr(std::same_as<T,bf16>) return N==4 || (N==8 && !(avx512_bf16<=A));
842 if constexpr(bytes==8) return simd_integer_element<T> && sizeof(T)<4;
843 if constexpr(bytes==16 || bytes==32) {
844 if constexpr(bytes==32 && !A.has(x86_feature::avx)) return false;
845 return std::same_as<T,double> || !(avx2<=A);
846 }
847 if constexpr(bytes==64 && A.has(x86_feature::avx512f)) {
848 if constexpr(std::same_as<T,double>) return true;
849 return !(kernel_base<=A) || (sizeof(T)<4 && !A.has(x86_feature::avx512bw));
850 }
851 return false;
852#else
853 return false;
854#endif
855 }
856 }();
857
858 template<std::size_t N,isa<> A> inline constexpr bool instruction_predicate_shape =
859 N>0 && N<=64 &&
860#if NATIVE_HOST_X86
861 A.has(x86_feature::sse2) && !((kernel_base<=A) &&
862 (N==1 || N==2 || N==3 || N==4 || N==8 || N==16 ||
863 ((N==32 || N==64) && A.has(x86_feature::avx512bw))));
864#elif NATIVE_HOST_NEON
865 (neon<=A);
866#else
867 false;
868#endif
869
870 template<class T,std::size_t N,isa<> A>
871 requires ordinary_simd_element<T> && instruction_storage_shape<T,N,A>
872 struct value_traits<simd<T,N,A>> {
873 static constexpr isa<> value=A;
874 static constexpr bool known=true;
875 static constexpr bool aggregate_default=false;
876 };
877 template<std::size_t N,isa<> A> requires instruction_predicate_shape<N,A>
878 struct value_traits<predicate<N,A>> {
879 static constexpr isa<> value=A;
880 static constexpr bool known=true;
881 static constexpr bool aggregate_default=false;
882 };
883
884 template<class T, std::size_t N> struct instruction_register {
885#if NATIVE_HOST_NEON
886 using type=std::conditional_t<std::same_as<T,double>,float64x2_t,
887 std::conditional_t<(sizeof(T)*N<=8),uint8x8_t,uint8x16_t>>;
888#elif NATIVE_HOST_X86
889 using type=std::conditional_t<std::same_as<T,float>,
890 std::conditional_t<(sizeof(T)*N<=16),__m128,std::conditional_t<(sizeof(T)*N==32),__m256,__m512>>,
891 std::conditional_t<std::same_as<T,double>,
892 std::conditional_t<(sizeof(T)*N<=16),__m128d,std::conditional_t<(sizeof(T)*N==32),__m256d,__m512d>>,
893 std::conditional_t<(sizeof(T)*N<=16),__m128i,std::conditional_t<(sizeof(T)*N==32),__m256i,__m512i>>>>;
894#endif
895 };
896 }
897
899 template<std::size_t N, isa<> A> requires detail::instruction_predicate_shape<N,A>
900 struct predicate<N,A> {
901 static constexpr isa<> architecture=A;
902 static constexpr std::size_t lanes=N;
903 using native_type=std::conditional_t<(N<=8),std::uint8_t,
904 std::conditional_t<(N<=16),std::uint16_t,std::conditional_t<(N<=32),std::uint32_t,std::uint64_t>>>;
905 using mask=predicate;
906 using mask_type=predicate;
907 static constexpr bool compact=true;
908 private:
909 native_type value_{};
910 static constexpr std::uint64_t active=[] { if constexpr(N==64) return ~std::uint64_t{}; else return (std::uint64_t{1}<<N)-1; }();
911 public:
913 native_inline constexpr predicate() noexcept=default;
915 explicit native_inline constexpr predicate(bool value) noexcept : value_(value?native_type(active):0) {}
917 static native_inline constexpr predicate from_bits(std::uint64_t bits) noexcept { predicate p; p.value_=native_type(bits&active); return p; }
919 static native_inline constexpr predicate from_bitset(std::uint64_t bits) noexcept { return from_bits(bits); }
921 static native_inline constexpr predicate from_native(native_type bits) noexcept { return from_bits(bits); }
923 native_inline constexpr native_type to_native() const noexcept { return value_; }
925 native_inline constexpr std::uint64_t bits() const noexcept { return value_; }
927 native_inline constexpr std::uint64_t to_bitset() const noexcept { return value_; }
929 friend native_inline constexpr bool any(predicate p) noexcept { return p.value_!=0; }
931 friend native_inline constexpr bool all(predicate p) noexcept { return p.value_==active; }
933 friend native_inline constexpr bool none(predicate p) noexcept { return p.value_==0; }
935 friend native_inline constexpr predicate operator~(predicate p) noexcept { return from_bits(~p.value_); }
937 friend native_inline constexpr predicate operator!(predicate p) noexcept { return ~p; }
939 friend native_inline constexpr predicate operator&(predicate a,predicate b) noexcept { return from_bits(a.value_&b.value_); }
941 friend native_inline constexpr predicate operator|(predicate a,predicate b) noexcept { return from_bits(a.value_|b.value_); }
943 friend native_inline constexpr predicate operator^(predicate a,predicate b) noexcept { return from_bits(a.value_^b.value_); }
945 friend native_inline constexpr predicate operator==(predicate a,predicate b) noexcept { return ~(a^b); }
947 friend native_inline constexpr predicate operator!=(predicate a,predicate b) noexcept { return a^b; }
949 native_inline constexpr predicate & operator&=(predicate b) noexcept { return *this=*this&b; }
951 native_inline constexpr predicate & operator|=(predicate b) noexcept { return *this=*this|b; }
953 native_inline constexpr predicate & operator^=(predicate b) noexcept { return *this=*this^b; }
955 friend native_inline constexpr predicate select(predicate p,predicate a,predicate b) noexcept { return (p&a)|(~p&b); }
956 };
957
961 template<class T, std::size_t N, isa<> A> requires detail::instruction_storage_shape<T,N,A>
962 struct alignas(typename detail::instruction_register<T,N>::type) simd<T,N,A> {
963 using value_type=T;
964 using register_type=simd;
965 using native_type=typename detail::instruction_register<T,N>::type;
966 static constexpr isa<> architecture=A;
967 static constexpr std::size_t lanes=N;
968 using mask=predicate<N,A>;
969 using mask_type=mask;
970 using predicate_type=mask;
971 using bits_type=simd<std::conditional_t<sizeof(T)==1,std::uint8_t,
972 std::conditional_t<sizeof(T)==2,std::uint16_t,
973 std::conditional_t<sizeof(T)==4,std::uint32_t,std::uint64_t>>>,N,A>;
974 template<class U> using rebind=simd<U,N,A>;
975 private:
976 native_type value_;
977 public:
978 // Clang may accept a storage record mixed with a builtin vector even
979 // without a native conversion operator. Reject half arithmetic explicitly.
980#define NATIVE_INSTRUCTION_HALF_REJECT_BINARY(OP) \
981 \
982 friend void operator OP(simd,simd) \
983 requires(std::same_as<T,fp16> || std::same_as<T,bf16>) = delete; \
984 \
985 template<class U> requires((std::same_as<T,fp16> || std::same_as<T,bf16>) && \
986 !std::same_as<std::remove_cvref_t<U>,simd>) \
987 friend void operator OP(simd,U) = delete; \
988 \
989 template<class U> requires((std::same_as<T,fp16> || std::same_as<T,bf16>) && \
990 !std::same_as<std::remove_cvref_t<U>,simd>) \
991 friend void operator OP(U,simd) = delete;
992 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(+)
993 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(-)
994 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(*)
995 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(/)
996 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(%)
997 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(&)
998 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(|)
999 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(^)
1000 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(<<)
1001 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(>>)
1002 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(==)
1003 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(!=)
1004 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(<)
1005 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(<=)
1006 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(>)
1007 NATIVE_INSTRUCTION_HALF_REJECT_BINARY(>=)
1008#undef NATIVE_INSTRUCTION_HALF_REJECT_BINARY
1009#define NATIVE_INSTRUCTION_HALF_REJECT_ASSIGN(OP) \
1010 \
1011 template<class U> requires(std::same_as<T,fp16> || std::same_as<T,bf16>) \
1012 void operator OP(U) = delete;
1013 NATIVE_INSTRUCTION_HALF_REJECT_ASSIGN(+=)
1014 NATIVE_INSTRUCTION_HALF_REJECT_ASSIGN(-=)
1015 NATIVE_INSTRUCTION_HALF_REJECT_ASSIGN(*=)
1016 NATIVE_INSTRUCTION_HALF_REJECT_ASSIGN(/=)
1017 NATIVE_INSTRUCTION_HALF_REJECT_ASSIGN(%=)
1018 NATIVE_INSTRUCTION_HALF_REJECT_ASSIGN(&=)
1019 NATIVE_INSTRUCTION_HALF_REJECT_ASSIGN(|=)
1020 NATIVE_INSTRUCTION_HALF_REJECT_ASSIGN(^=)
1021 NATIVE_INSTRUCTION_HALF_REJECT_ASSIGN(<<=)
1022 NATIVE_INSTRUCTION_HALF_REJECT_ASSIGN(>>=)
1023#undef NATIVE_INSTRUCTION_HALF_REJECT_ASSIGN
1025 friend void operator+(simd) requires(std::same_as<T,fp16> || std::same_as<T,bf16>) = delete;
1027 friend void operator-(simd) requires(std::same_as<T,fp16> || std::same_as<T,bf16>) = delete;
1029 friend void operator~(simd) requires(std::same_as<T,fp16> || std::same_as<T,bf16>) = delete;
1031 friend void operator!(simd) requires(std::same_as<T,fp16> || std::same_as<T,bf16>) = delete;
1033 constexpr simd() noexcept=default;
1035 native_inline constexpr explicit simd(T value) noexcept {
1036 std::array<T,N> values;
1037 values.fill(value);
1038 *this=load(values.data());
1039 }
1040
1041 native_inline constexpr explicit simd(std::array<T,N> const & values) noexcept : simd(load(values.data())) {}
1043 template<class... U> requires(sizeof...(U)==N && (std::same_as<U,T> && ...))
1044 native_inline constexpr simd(U... values) noexcept : simd(std::array<T,N>{values...}) {}
1045#if NATIVE_HOST_X86
1046 // Native vector arguments and returns must carry their register ABI even
1047 // when an always-inline caller has already enabled that target.
1050 native_type to_native() const noexcept requires(sizeof(native_type)==16) { return value_; }
1052 native_nodiscard static native_inline constexpr native_target("sse2")
1053 simd from_native(native_type value) noexcept requires(sizeof(native_type)==16) {
1054 simd result; result.value_=value; return result;
1055 }
1056
1057 native_nodiscard static native_inline constexpr native_target("sse2")
1058 simd unsafe_from_native(native_type value) noexcept requires(sizeof(native_type)==16) {
1059 simd result; result.value_=value; return result;
1060 }
1061
1063 native_type to_native() const noexcept requires(sizeof(native_type)==32) { return value_; }
1065 native_nodiscard static native_inline constexpr native_target("avx")
1066 simd from_native(native_type value) noexcept requires(sizeof(native_type)==32) {
1067 simd result; result.value_=value; return result;
1068 }
1069
1070 native_nodiscard static native_inline constexpr native_target("avx")
1071 simd unsafe_from_native(native_type value) noexcept requires(sizeof(native_type)==32) {
1072 simd result; result.value_=value; return result;
1073 }
1074
1075 native_nodiscard native_inline constexpr native_target("avx512f")
1076 native_type to_native() const noexcept requires(sizeof(native_type)==64) { return value_; }
1078 native_nodiscard static native_inline constexpr native_target("avx512f")
1079 simd from_native(native_type value) noexcept requires(sizeof(native_type)==64) {
1080 simd result; result.value_=value; return result;
1081 }
1082
1083 native_nodiscard static native_inline constexpr native_target("avx512f")
1084 simd unsafe_from_native(native_type value) noexcept requires(sizeof(native_type)==64) {
1085 simd result; result.value_=value; return result;
1086 }
1087#else
1089 native_nodiscard native_inline constexpr native_type to_native() const noexcept { return value_; }
1091 native_nodiscard static native_inline constexpr simd from_native(native_type value) noexcept {
1092 simd result; result.value_=value; return result;
1093 }
1094#endif
1096 template<std::size_t Alignment=1>
1097 native_nodiscard static native_inline constexpr simd load_memory(T const * p) noexcept {
1098 static_assert(Alignment>0 && (Alignment&(Alignment-1))==0);
1099 simd result{};
1100 if consteval {
1101 std::array<T,sizeof(native_type)/sizeof(T)> values{};
1102 for(std::size_t i=0;i<N;++i) values[i]=p[i];
1103 result.value_=std::bit_cast<native_type>(values);
1104 } else { std::memcpy(&result.value_,p,sizeof(T)*N); }
1105 return result;
1106 }
1107
1108 template<std::size_t Alignment=1>
1109 native_inline constexpr void store_memory(T * p) const noexcept {
1110 static_assert(Alignment>0 && (Alignment&(Alignment-1))==0);
1111 if consteval {
1112 auto values=std::bit_cast<std::array<T,sizeof(native_type)/sizeof(T)>>(value_);
1113 for(std::size_t i=0;i<N;++i) p[i]=values[i];
1114 } else { std::memcpy(p,&value_,sizeof(T)*N); }
1115 }
1116
1117 native_nodiscard static native_inline constexpr simd load(T const * p) noexcept { return load_memory(p); }
1119 native_nodiscard static native_inline constexpr simd loadu(T const * p) noexcept { return load(p); }
1121 native_inline constexpr void store(T * p) const noexcept { store_memory(p); }
1123 native_inline constexpr void storeu(T * p) const noexcept { store(p); }
1125 native_nodiscard static native_inline constexpr simd load_partial(T const * p,std::size_t n,T fill=T{}) noexcept native_diagnose_if(n > simd::lanes,"partial SIMD count exceeds the lane count") {
1126 assert(n<=N);
1127 std::array<T,N> values; values.fill(fill);
1128 if consteval {
1129 for(std::size_t i=0;i<n;++i) values[i]=p[i];
1130 } else { if(n) std::memcpy(values.data(),p,n*sizeof(T)); }
1131 return load(values.data());
1132 }
1133
1134 native_inline constexpr void store_partial(T * p,std::size_t n) const noexcept native_diagnose_if(n > simd::lanes,"partial SIMD count exceeds the lane count") {
1135 assert(n<=N);
1136 if consteval {
1137 if(n) {
1138 auto values=std::bit_cast<std::array<T,sizeof(native_type)/sizeof(T)>>(value_);
1139 for(std::size_t i=0;i<n;++i) p[i]=values[i];
1140 }
1141 } else { if(n) std::memcpy(p,&value_,n*sizeof(T)); }
1142 }
1143
1144 native_nodiscard native_inline constexpr bits_type bits() const noexcept requires(std::same_as<T,fp16> || std::same_as<T,bf16>) {
1145 return bits_type::from_native(std::bit_cast<typename bits_type::native_type>(value_));
1146 }
1147
1148 native_nodiscard native_inline constexpr bits_type to_bits() const noexcept requires(std::same_as<T,fp16> || std::same_as<T,bf16>) { return bits(); }
1150 native_nodiscard static native_inline constexpr simd from_bits(bits_type words) noexcept requires(std::same_as<T,fp16> || std::same_as<T,bf16>) {
1151 return from_native(std::bit_cast<native_type>(words.to_native()));
1152 }
1153
1154 native_nodiscard static native_inline constexpr simd load_bits(std::uint16_t const * p) noexcept requires(std::same_as<T,fp16> || std::same_as<T,bf16>) {
1155 if consteval { return from_bits(bits_type::load(p)); }
1156 else { simd result{}; std::memcpy(&result.value_,p,2*N); return result; }
1157 }
1158
1159 native_inline constexpr void store_bits(std::uint16_t * p) const noexcept requires(std::same_as<T,fp16> || std::same_as<T,bf16>) {
1160 if consteval { bits().store(p); }
1161 else { std::memcpy(p,&value_,2*N); }
1162 }
1163 };
1164}
1165
1166#endif
#define native_diagnose_if(condition, message)
Reject a call when Clang can prove that its arguments violate a precondition.
Definition attributes.h:50
#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
Scalar half storage, numeric limits and legacy scalar predicates.
Architecture-tagged vectors, register packs and supporting value types. Native arithmetic follows its...
constexpr simd< float, N/2, Arch > dot2(simd< bf16, N, Arch > a, simd< bf16, N, Arch > b, simd< float, N/2, Arch > accumulator) noexcept
constexpr simd< fp16, 32, Arch > fma(simd< fp16, 32, Arch > a, simd< fp16, 32, Arch > b, simd< fp16, 32, Arch > c) noexcept
Standard-library adaptations documented here for SIMD value types.
static constexpr bf16 from_bits(uint16_t data) noexcept
Adopt the exact sixteen representation bits without normalization.
static constexpr fp16 from_bits(uint16_t data) noexcept
Adopt the exact sixteen representation bits without normalization.
constexpr std::uint64_t to_bitset() const noexcept
Return the logical lane bitset.
static constexpr predicate from_bits(std::uint64_t bits) noexcept
Read one bit per lane and clear bits above the logical lane count.
friend constexpr bool all(predicate p) noexcept
Test whether every logical lane is set.
static constexpr predicate from_bitset(std::uint64_t bits) noexcept
Construct from the logical lane bitset.
friend constexpr predicate operator~(predicate p) noexcept
Complement logical lanes, leaving padding clear.
constexpr std::uint64_t bits() const noexcept
Return one bit per logical lane.
friend constexpr predicate operator==(predicate a, predicate b) noexcept
Mark lanes whose truth values agree.
friend constexpr bool none(predicate p) noexcept
Test whether every logical lane is clear.
constexpr native_type to_native() const noexcept
Return the compact implementation representation.
constexpr predicate & operator^=(predicate b) noexcept
Toggle lanes present in another mask.
friend constexpr predicate operator!(predicate p) noexcept
Complement each logical lane.
static constexpr predicate from_native(native_type bits) noexcept
Adopt the compact representation, clearing unused bits.
friend constexpr predicate operator&(predicate a, predicate b) noexcept
Intersect the logical lane masks.
friend constexpr bool any(predicate p) noexcept
Test whether any logical lane is set.
friend constexpr predicate operator^(predicate a, predicate b) noexcept
Toggle lanes present in exactly one operand.
friend constexpr predicate select(predicate p, predicate a, predicate b) noexcept
Choose each mask lane from a or b according to p.
friend constexpr predicate operator!=(predicate a, predicate b) noexcept
Mark lanes whose truth values differ.
constexpr predicate & operator&=(predicate b) noexcept
Intersect with another mask in place.
friend constexpr predicate operator|(predicate a, predicate b) noexcept
Unite the logical lane masks.
constexpr predicate() noexcept=default
Construct an empty mask.
constexpr predicate & operator|=(predicate b) noexcept
Unite with another mask in place.
static constexpr simd load(T const *p) noexcept
Read exactly N elements with their natural alignment.
constexpr void store_memory(T *p) const noexcept
Write exactly N objects; Alignment is a caller promise.
friend void operator!(simd)=delete
Half storage does not define a predicate conversion.
friend void operator-(simd)=delete
Reject unary arithmetic on half instruction storage.
constexpr simd() noexcept=default
Default initialization leaves storage unspecified; braces zero it.
constexpr void store_bits(std::uint16_t *p) const noexcept
Write N unsigned half representations without numerical conversion.
constexpr void store_partial(T *p, std::size_t n) const noexcept
Write the first n <= N elements; a null pointer is valid for n == 0.
static constexpr simd load_bits(std::uint16_t const *p) noexcept
Read N unsigned half representations without numerical conversion.
constexpr native_type to_native() const noexcept
Bridge to the implementation register without numerical conversion.
static constexpr simd unsafe_from_native(native_type value) noexcept
Synonym for from_native; these storage-only shapes do not normalize padding.
static constexpr simd loadu(T const *p) noexcept
Synonym for load; register alignment is unnecessary.
constexpr simd(std::array< T, N > const &values) noexcept
Copy the array elements in lane order.
constexpr bits_type bits() const noexcept
Return each half lane as its unchanged unsigned representation.
friend void operator~(simd)=delete
Use bits() for explicit bitwise operations on half representations.
static constexpr simd from_native(native_type value) noexcept
Adopt register bits unchanged; unused physical bytes are unspecified.
friend void operator+(simd)=delete
Reject unary arithmetic on half instruction storage.
static constexpr simd load_memory(T const *p) noexcept
Read exactly N objects, without requiring register-width alignment.
constexpr bits_type to_bits() const noexcept
Synonym for bits; no floating-point conversion occurs.
constexpr void store(T *p) const noexcept
Write exactly N elements in lane order.
static constexpr simd load_partial(T const *p, std::size_t n, T fill=T{}) noexcept
Read n <= N elements and fill the remainder; a null pointer is valid for n == 0.
static constexpr simd from_bits(bits_type words) noexcept
Interpret unsigned words as half representations without conversion.
constexpr void storeu(T *p) const noexcept
Synonym for store; register alignment is unnecessary.
constexpr void store_partial(bf16 *p, std::size_t n) const noexcept
simd() noexcept=default
Default initialization leaves storage unspecified; braces zero it.
simd< std::uint16_t, 8, architecture > bits_type
Unsigned 16-bit lanes in the same profile and lane order.
static constexpr simd from_bits(bits_type value) noexcept
Interpret each unsigned lane as a BF16 representation without changing its bits.
static constexpr simd load_bits(std::uint16_t const *p) noexcept
static constexpr std::size_t lanes
Number of logical BF16 lanes, with no padding lanes.
constexpr bits_type to_bits() const noexcept
Synonym for bits(); this is a representation bridge, not a numeric conversion.
constexpr void store(bf16 *p) const noexcept
Store 8 BF16 objects with the default alignment contract of store_memory().
constexpr native_type to_native() const noexcept
Return all lane bits as a native register, without conversion or lane reordering.
constexpr void store_memory(bf16 *p) const noexcept
static constexpr simd loadu(bf16 const *p) noexcept
Synonym for load(); no register-width alignment is required.
simd< T, 8, architecture > rebind
constexpr bits_type bits() const noexcept
Return the 16-bit representation of each lane in an unsigned vector.
static constexpr simd load_memory(bf16 const *p) noexcept
simd< mask16, 8, architecture > mask
Full-register predicate with zero or all-one 16-bit lanes.
constexpr simd(native_type value) noexcept
Adopt a native register without conversion or representation changes.
constexpr void store_bits(std::uint16_t *p) const noexcept
static constexpr isa architecture
The distinct compile-time NEON_BF16 instruction profile.
mask predicate_type
Explicit predicate spelling for the same full-register mask type.
bf16 value_type
Scalar storage element; each lane retains all 16 representation bits.
static constexpr simd load(bf16 const *p) noexcept
Load 8 BF16 objects with the default alignment contract of load_memory().
constexpr void storeu(bf16 *p) const noexcept
Synonym for store(); no register-width alignment is required.
static constexpr simd from_native(native_type value) noexcept
Copy a native BF16 register into this vector, preserving every representation bit.
bfloat16x8_t native_type
Native 128-bit BF16 register representation; native bridges copy bits.
constexpr simd(std::array< bf16, lanes > const &values) noexcept
Copy array element i into lane i without conversion or representation changes.
simd register_type
This one-register vector type, for generic register-based algorithms.
mask mask_type
Generic mask spelling for the full-register lane mask.
simd< mask16, 8, architecture > vector_mask_type
Full-register mask shape with zero or all-one 16-bit lanes.
static constexpr simd load_partial(bf16 const *p, std::size_t n, bf16 fill=bf16::from_bits(0)) noexcept
static constexpr std::size_t lanes
Number of logical BF16 lanes, with no padding lanes.
static constexpr simd load_memory(bf16 const *p) noexcept
static constexpr simd loadu(bf16 const *p) noexcept
Synonym for load(); no register-width alignment is required.
constexpr void store_memory(bf16 *p) const noexcept
static constexpr simd load_bits(std::uint16_t const *p) noexcept
constexpr simd(native_type value) noexcept
Adopt a native register without conversion or representation changes.
constexpr void store_bits(std::uint16_t *p) const noexcept
std::conditional_t< N==8, __m128bh, std::conditional_t< N==16, __m256bh, __m512bh > > native_type
Native register-width BF16 register representation; native bridges copy bits.
constexpr simd(std::array< bf16, lanes > const &values) noexcept
Copy array element i into lane i without conversion or representation changes.
constexpr void store_partial(bf16 *p, std::size_t n) const noexcept
simd< mask16, N, architecture > vector_mask_type
Full-register mask shape with zero or all-one 16-bit lanes.
bf16 value_type
Scalar storage element; each lane retains all 16 representation bits.
static constexpr isa architecture
The distinct compile-time AVX512_BF16 instruction profile.
constexpr bits_type to_bits() const noexcept
Synonym for bits(); this is a representation bridge, not a numeric conversion.
constexpr void storeu(bf16 *p) const noexcept
Synonym for store(); no register-width alignment is required.
static constexpr simd load(bf16 const *p) noexcept
Load N BF16 objects with the default alignment contract of load_memory().
static constexpr simd load_partial(bf16 const *p, std::size_t n, bf16 fill=bf16::from_bits(0)) noexcept
constexpr bits_type bits() const noexcept
Return the 16-bit representation of each lane in an unsigned vector.
constexpr void store(bf16 *p) const noexcept
Store N BF16 objects with the default alignment contract of store_memory().
simd< std::uint16_t, N, architecture > bits_type
Unsigned 16-bit lanes in the same profile and lane order.
mask mask_type
Generic mask spelling for the compact lane predicate.
static constexpr simd from_bits(bits_type value) noexcept
Interpret each unsigned lane as a BF16 representation without changing its bits.
predicate< N, architecture > mask
Compact predicate with bit i selecting BF16 lane i.
mask predicate_type
Explicit predicate spelling for the same compact mask type.
simd< T, N, architecture > rebind
constexpr native_type to_native() const noexcept
Return all lane bits as a native register, without conversion or lane reordering.
simd register_type
This one-register vector type, for generic register-based algorithms.
simd() noexcept=default
Default initialization leaves storage unspecified; braces zero it.
static constexpr simd from_native(native_type value) noexcept
Copy a native BF16 register into this vector, preserving every representation bit.
constexpr bits_type bits() const noexcept
Return the 16-bit representation of each lane in an unsigned vector.
constexpr void store_partial(fp16 *p, std::size_t n) const noexcept
constexpr void store_bits(std::uint16_t *p) const noexcept
friend constexpr simd sqrt(simd a) noexcept
simd< T, 32, architecture > rebind
friend constexpr mask operator>=(simd a, simd b) noexcept
Ordered lane greater-or-equal, with native less-or-equal operands reversed.
static constexpr simd load_bits(std::uint16_t const *p) noexcept
simd register_type
This one-register vector type, for generic register-based algorithms.
constexpr void store(fp16 *p) const noexcept
Store 32 FP16 objects with the default alignment contract of store_memory().
constexpr bits_type to_bits() const noexcept
Synonym for bits(); this is a representation bridge, not a numeric conversion.
friend constexpr simd operator/(simd a, simd b) noexcept
simd< std::uint16_t, 32, architecture > bits_type
Unsigned 16-bit lanes in the same profile and lane order.
static constexpr simd from_native(native_type value) noexcept
Copy a native FP16 register into this vector, preserving every representation bit.
friend constexpr mask operator==(simd a, simd b) noexcept
constexpr simd(native_type value) noexcept
Adopt a native register without conversion or representation changes.
friend constexpr simd select(mask m, simd a, simd b) noexcept
static constexpr simd load_memory(fp16 const *p) noexcept
static constexpr simd from_bits(bits_type value) noexcept
Interpret each unsigned lane as a FP16 representation without changing its bits.
predicate< 32, architecture > mask
Compact predicate with bit i selecting FP16 lane i.
fp16 value_type
Scalar storage element; each lane retains all 16 representation bits.
static constexpr isa architecture
The distinct compile-time AVX512_FP16 instruction profile.
friend constexpr mask operator!=(simd a, simd b) noexcept
Lane inequality, true for unordered NaN operands; complements native equality.
static constexpr simd load_partial(fp16 const *p, std::size_t n, fp16 fill=fp16::from_bits(0)) noexcept
static constexpr simd load(fp16 const *p) noexcept
Load 32 FP16 objects with the default alignment contract of load_memory().
friend constexpr mask operator<=(simd a, simd b) noexcept
Ordered lane less-or-equal. NaNs compare false; native MXCSR exception semantics apply.
constexpr void store_memory(fp16 *p) const noexcept
static constexpr simd loadu(fp16 const *p) noexcept
Synonym for load(); no register-width alignment is required.
friend constexpr simd operator-(simd a) noexcept
Toggle every sign bit, preserving all payload bits without arithmetic exceptions.
constexpr simd(std::array< fp16, lanes > const &values) noexcept
Copy array element i into lane i without conversion or representation changes.
constexpr void storeu(fp16 *p) const noexcept
Synonym for store(); no register-width alignment is required.
friend constexpr simd operator*(simd a, simd b) noexcept
Multiply corresponding half lanes, rounding directly under the caller's MXCSR rounding control.
simd< mask16, 32, architecture > vector_mask_type
Full-register mask shape with zero or all-one 16-bit lanes.
simd() noexcept=default
Default initialization leaves storage unspecified; braces zero it.
constexpr native_type to_native() const noexcept
Return all lane bits as a native register, without conversion or lane reordering.
mask mask_type
Generic mask spelling for the compact lane predicate.
friend constexpr simd operator+(simd a, simd b) noexcept
Add corresponding half lanes, rounding directly under the caller's MXCSR rounding control.
mask predicate_type
Explicit predicate spelling for the same compact predicate type.
friend constexpr simd operator-(simd a, simd b) noexcept
Subtract corresponding half lanes, rounding directly under the caller's MXCSR rounding control.
friend constexpr mask operator<(simd a, simd b) noexcept
Ordered lane less-than. NaNs compare false; native MXCSR exception semantics apply.
__m512h native_type
Native 512-bit FP16 register representation; native bridges copy bits.
static constexpr std::size_t lanes
Number of logical FP16 lanes, with no padding lanes.
friend constexpr mask operator>(simd a, simd b) noexcept
Ordered lane greater-than, with the native less-than operands reversed.
static constexpr std::size_t lanes
Number of logical FP16 lanes, with no padding lanes.
mask mask_type
Generic mask spelling for the full-register lane mask.
friend constexpr simd sqrt(simd a) noexcept
friend constexpr mask operator>=(simd a, simd b) noexcept
Ordered lane greater-or-equal, with native less-or-equal operands reversed.
simd< T, 8, architecture > rebind
static constexpr simd loadu(fp16 const *p) noexcept
Synonym for load(); no register-width alignment is required.
static constexpr simd load_memory(fp16 const *p) noexcept
static constexpr simd from_bits(bits_type value) noexcept
Interpret each unsigned lane as a FP16 representation without changing its bits.
constexpr void store(fp16 *p) const noexcept
Store 8 FP16 objects with the default alignment contract of store_memory().
friend constexpr simd operator/(simd a, simd b) noexcept
friend constexpr mask operator==(simd a, simd b) noexcept
simd< mask16, 8, architecture > mask
Full-register predicate with zero or all-one bits in each 16-bit lane.
friend constexpr simd select(mask m, simd a, simd b) noexcept
simd() noexcept=default
Default initialization leaves storage unspecified; braces zero it.
float16x8_t native_type
Native 128-bit FP16 register representation; native bridges copy bits.
friend constexpr mask operator!=(simd a, simd b) noexcept
Lane inequality, true for unordered NaN operands; complements native equality.
friend constexpr mask operator<=(simd a, simd b) noexcept
Ordered lane less-or-equal. NaNs compare false; native FPCR/FPSR semantics apply.
mask predicate_type
Explicit predicate spelling for the same full-register mask type.
static constexpr simd from_native(native_type value) noexcept
Copy a native FP16 register into this vector, preserving every representation bit.
friend constexpr simd operator-(simd a) noexcept
Apply native FNEG to each half lane; no scalar half-to-float conversion occurs.
static constexpr simd load_partial(fp16 const *p, std::size_t n, fp16 fill=fp16::from_bits(0)) noexcept
static constexpr simd load(fp16 const *p) noexcept
Load 8 FP16 objects with the default alignment contract of load_memory().
constexpr native_type to_native() const noexcept
Return all lane bits as a native register, without conversion or lane reordering.
fp16 value_type
Scalar storage element; each lane retains all 16 representation bits.
friend constexpr simd operator*(simd a, simd b) noexcept
Multiply corresponding half lanes, rounding directly under the caller's FPCR.
constexpr bits_type bits() const noexcept
Return the 16-bit representation of each lane in an unsigned vector.
constexpr void store_memory(fp16 *p) const noexcept
constexpr void store_bits(std::uint16_t *p) const noexcept
friend constexpr simd operator+(simd a, simd b) noexcept
Add corresponding half lanes, rounding directly under the caller's FPCR.
constexpr void store_partial(fp16 *p, std::size_t n) const noexcept
simd< mask16, 8, architecture > vector_mask_type
Full-register mask shape with zero or all-one 16-bit lanes.
friend constexpr simd operator-(simd a, simd b) noexcept
Subtract corresponding half lanes, rounding directly under the caller's FPCR.
constexpr bits_type to_bits() const noexcept
Synonym for bits(); this is a representation bridge, not a numeric conversion.
constexpr simd(std::array< fp16, lanes > const &values) noexcept
Copy array element i into lane i without conversion or representation changes.
static constexpr simd load_bits(std::uint16_t const *p) noexcept
simd< std::uint16_t, 8, architecture > bits_type
Unsigned 16-bit lanes in the same profile and lane order.
friend constexpr mask operator<(simd a, simd b) noexcept
Ordered lane less-than. NaNs compare false; native FPCR/FPSR semantics apply.
constexpr simd(native_type value) noexcept
Adopt a native register without conversion or representation changes.
static constexpr isa architecture
The distinct compile-time NEON_FP16 instruction profile.
constexpr void storeu(fp16 *p) const noexcept
Synonym for store(); no register-width alignment is required.
simd register_type
This one-register vector type, for generic register-based algorithms.
friend constexpr mask operator>(simd a, simd b) noexcept
Ordered lane greater-than, with the native less-than operands reversed.
Omitted architecture arguments use the native.simd provider's baseline.