native 0.0.1
Vectors, masks and wide register packs for C++26
Loading...
Searching...
No Matches
neon_constant.h
1// SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0
2#pragma once
3#include "native/arm/detail/constant_lanes.h"
4#include <limits>
5
6namespace native::detail::arm_neon_constant {
7 template<class T> constexpr T add(T a, T b) noexcept {
8 constexpr auto lo = std::numeric_limits<T>::min();
9 constexpr auto hi = std::numeric_limits<T>::max();
10 if constexpr (std::is_signed_v<T>) {
11 if (b > 0 && a > hi - b)
12 return hi;
13 if (b < 0 && a < lo - b)
14 return lo;
15 } else if (a > hi - b) {
16 return hi;
17 }
18 return T(a + b);
19 }
20
21 template<class T> constexpr T sub(T a, T b) noexcept {
22 constexpr auto lo = std::numeric_limits<T>::min();
23 constexpr auto hi = std::numeric_limits<T>::max();
24 if constexpr (std::is_signed_v<T>) {
25 if (b < 0 && a > hi + b)
26 return hi;
27 if (b > 0 && a < lo + b)
28 return lo;
29 } else if (a < b) {
30 return 0;
31 }
32 return T(a - b);
33 }
34
35 template<class To, class From> constexpr To narrow(From a) noexcept {
36 if (a < From(std::numeric_limits<To>::min()))
37 return std::numeric_limits<To>::min();
38 if (a > From(std::numeric_limits<To>::max()))
39 return std::numeric_limits<To>::max();
40 return To(a);
41 }
42
43 template<bool Round, class T> constexpr T multiply_high(T a, T b) noexcept {
44 constexpr unsigned width = sizeof(T) * 8;
45 auto product = std::int64_t(a) * b;
46 if constexpr (Round)
47 product += std::int64_t{1} << (width - 2);
48 return narrow<T>(product >> (width - 1));
49 }
50
51 template<bool Round, bool Saturate, class T, class S>
52 constexpr T shift(T value, S count) noexcept {
53 using U = std::make_unsigned_t<T>;
54 constexpr unsigned width = sizeof(T) * 8;
55 // The architecture consumes only the signed low byte of each count lane.
56 int amount = std::bit_cast<std::int8_t>(std::uint8_t(count));
57 if (amount >= 0) {
58 unsigned left = unsigned(amount);
59 if (left >= width) {
60 if constexpr (Saturate) {
61 if (value != 0)
62 return value < 0 ? std::numeric_limits<T>::min() : std::numeric_limits<T>::max();
63 }
64 return 0;
65 }
66 if constexpr (Saturate) {
67 if (value > (std::numeric_limits<T>::max() >> left))
68 return std::numeric_limits<T>::max();
69 if constexpr (std::is_signed_v<T>)
70 if (value < (std::numeric_limits<T>::min() >> left))
71 return std::numeric_limits<T>::min();
72 }
73 return std::bit_cast<T>(U(std::uint64_t(U(value)) << left));
74 }
75 unsigned right = unsigned(-amount);
76 if (right >= width) {
77 if constexpr (Round) {
78 if constexpr (std::is_unsigned_v<T>)
79 if (right == width)
80 return T(value >> (width - 1));
81 return 0;
82 } else {
83 return value < 0 ? T(-1) : T(0);
84 }
85 }
86 T result = T(value >> right);
87 if constexpr (Round)
88 result = T(result + T((U(value) >> (right - 1)) & 1));
89 return result;
90 }
91
92 template<class V, class W, class F> constexpr V binary(V a, W b, F operation) noexcept {
93 auto left = arm_constant::lanes(a);
94 auto right = arm_constant::lanes(b);
95 for (unsigned i = 0; i < V::lanes; ++i)
96 left[i] = operation(left[i], right[i]);
97 return arm_constant::pack<V>(left);
98 }
99
100 template<class Result, class V> constexpr Result narrow_low(V input) noexcept {
101 auto source = arm_constant::lanes(input);
102 std::array<typename Result::value_type, Result::lanes> result{};
103 for (unsigned i = 0; i < Result::lanes; ++i)
104 result[i] = narrow<typename Result::value_type>(source[i]);
105 return arm_constant::pack<Result>(result);
106 }
107
108 template<class Result, class Low, class V>
109 constexpr Result narrow_high(Low low, V input) noexcept {
110 auto bottom = arm_constant::lanes(low);
111 auto source = arm_constant::lanes(input);
112 std::array<typename Result::value_type, Result::lanes> result{};
113 for (unsigned i = 0; i < Low::lanes; ++i) {
114 result[i] = bottom[i];
115 result[i + Low::lanes] = narrow<typename Result::value_type>(source[i]);
116 }
117 return arm_constant::pack<Result>(result);
118 }
119} // namespace native::detail::arm_neon_constant
constexpr simd< T, N, A > max(simd< T, N, A > a, simd< T, N, A > b) noexcept
Minimum/maximum; floating NaNs propagate and signed zeros follow WebAssembly rules.