1 // Copyright 2017 Google Inc. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 // WARNING: this is a "restricted" source file; avoid including any headers
16 // unless they are also restricted. See arch_specific.h for details.
17
18 #include "highwayhash/vector_test_target.h"
19
20 #include "highwayhash/arch_specific.h"
21
22 #ifndef HH_DISABLE_TARGET_SPECIFIC
23 #if HH_TARGET == HH_TARGET_AVX2
24 #include "highwayhash/vector256.h"
25 #elif HH_TARGET == HH_TARGET_SSE41
26 #include "highwayhash/vector128.h"
27 #elif HH_TARGET == HH_TARGET_NEON
28 #include "highwayhash/vector_neon.h"
29 #elif HH_TARGET == HH_TARGET_Portable
30 #include "highwayhash/scalar.h"
31 #else
32 #error "Unknown target, add its include here."
33 #endif
34
35 namespace highwayhash {
36 namespace HH_TARGET_NAME {
37 namespace {
38
39 #if HH_TARGET == HH_TARGET_AVX2
40 template <typename T>
41 using V = V256<T>;
42 #elif HH_TARGET == HH_TARGET_SSE41 || HH_TARGET == HH_TARGET_NEON
43 template <typename T>
44 using V = V128<T>;
45 #elif HH_TARGET == HH_TARGET_Portable
46 template <typename T>
47 using V = Scalar<T>;
48 #else
49 #error "Unknown target, add its vector typedef here."
50 #endif
51
52 template <class T>
NotifyIfUnequal(const V<T> & v,const T expected,const size_t line,const HHNotify notify)53 void NotifyIfUnequal(const V<T>& v, const T expected, const size_t line,
54 const HHNotify notify) {
55 T lanes[V<T>::N] HH_ALIGNAS(32);
56 Store(v, lanes);
57 for (size_t i = 0; i < V<T>::N; ++i) {
58 if (lanes[i] != expected) {
59 notify(TargetName(HH_TARGET), (line << 16) | (i << 8) | sizeof(T));
60 }
61 }
62 }
63
64 template <class T>
NotifyIfUnequal(const T & t,const T expected,const size_t line,const HHNotify notify)65 void NotifyIfUnequal(const T& t, const T expected, const size_t line,
66 const HHNotify notify) {
67 if (t != expected) {
68 notify(TargetName(HH_TARGET), (line << 16) | sizeof(T));
69 }
70 }
71
72 // MaxValue<T>()() replaces std::numeric_limits<T>::max().
73 template <typename T>
74 struct MaxValue;
75 template <>
76 struct MaxValue<uint8_t> {
operator ()highwayhash::HH_TARGET_NAME::__anonde88c1440111::MaxValue77 constexpr uint8_t operator()() const { return 0xFFu; }
78 };
79 template <>
80 struct MaxValue<uint16_t> {
operator ()highwayhash::HH_TARGET_NAME::__anonde88c1440111::MaxValue81 constexpr uint16_t operator()() const { return 0xFFFFu; }
82 };
83 template <>
84 struct MaxValue<uint32_t> {
operator ()highwayhash::HH_TARGET_NAME::__anonde88c1440111::MaxValue85 constexpr uint32_t operator()() const { return 0xFFFFFFFFu; }
86 };
87 template <>
88 struct MaxValue<uint64_t> {
operator ()highwayhash::HH_TARGET_NAME::__anonde88c1440111::MaxValue89 constexpr uint64_t operator()() const { return 0xFFFFFFFFFFFFFFFFull; }
90 };
91
92 template <typename T>
TestMembersAndBinaryOperatorsExceptShifts(const HHNotify notify)93 void TestMembersAndBinaryOperatorsExceptShifts(const HHNotify notify) {
94 // uninitialized
95 V<T> v;
96
97 // broadcast
98 const V<T> v2(2);
99 NotifyIfUnequal(v2, T(2), __LINE__, notify);
100
101 // assign from V
102 const V<T> v3(3);
103 V<T> v3b;
104 v3b = v3;
105 NotifyIfUnequal(v3b, T(3), __LINE__, notify);
106
107 // equal
108 const V<T> veq(v3 == v3b);
109 NotifyIfUnequal(veq, MaxValue<T>()(), __LINE__, notify);
110
111 // Copying to, and constructing from intrinsic yields same result.
112 typename V<T>::Intrinsic nv2 = v2;
113 V<T> v2b(nv2);
114 NotifyIfUnequal(v2b, T(2), __LINE__, notify);
115
116 // .. assignment also works.
117 V<T> v2c;
118 v2c = nv2;
119 NotifyIfUnequal(v2c, T(2), __LINE__, notify);
120
121 const V<T> add = v2 + v3;
122 NotifyIfUnequal(add, T(5), __LINE__, notify);
123
124 const V<T> sub = v3 - v2;
125 NotifyIfUnequal(sub, T(1), __LINE__, notify);
126
127 const V<T> vand = v3 & v2;
128 NotifyIfUnequal(vand, T(2), __LINE__, notify);
129
130 const V<T> vor = add | v2;
131 NotifyIfUnequal(vor, T(7), __LINE__, notify);
132
133 const V<T> vxor = v3 ^ v2;
134 NotifyIfUnequal(vxor, T(1), __LINE__, notify);
135 }
136
137 // SSE does not allow shifting uint8_t, so instantiate for all other types.
138 template <class T>
TestShifts(const HHNotify notify)139 void TestShifts(const HHNotify notify) {
140 const V<T> v1(1);
141 // Shifting out of right side => zero
142 NotifyIfUnequal(v1 >> 1, T(0), __LINE__, notify);
143
144 // Simple left shift
145 NotifyIfUnequal(v1 << 1, T(2), __LINE__, notify);
146
147 // Sign bit
148 constexpr int kSign = (sizeof(T) * 8) - 1;
149 constexpr T max = MaxValue<T>()();
150 constexpr T sign = ~(max >> 1);
151 NotifyIfUnequal(v1 << kSign, sign, __LINE__, notify);
152
153 // Shifting out of left side => zero
154 NotifyIfUnequal(v1 << (kSign + 1), T(0), __LINE__, notify);
155 }
156
157 template <class T>
TestLoadStore(const HHNotify notify)158 void TestLoadStore(const HHNotify notify) {
159 const size_t n = V<T>::N;
160 T lanes[2 * n] HH_ALIGNAS(32);
161 for (size_t i = 0; i < n; ++i) {
162 lanes[i] = 4;
163 }
164 for (size_t i = n; i < 2 * n; ++i) {
165 lanes[i] = 5;
166 }
167 // Aligned load
168 const V<T> v4 = Load<V<T>>(lanes);
169 NotifyIfUnequal(v4, T(4), __LINE__, notify);
170
171 // Aligned store
172 T lanes4[n] HH_ALIGNAS(32);
173 Store(v4, lanes4);
174 NotifyIfUnequal(Load<V<T>>(lanes4), T(4), __LINE__, notify);
175
176 // Unaligned load
177 const V<T> vu = LoadUnaligned<V<T>>(lanes + 1);
178 Store(vu, lanes4);
179 NotifyIfUnequal(lanes4[n - 1], T(5), __LINE__, notify);
180 for (size_t i = 1; i < n - 1; ++i) {
181 NotifyIfUnequal(lanes4[i], T(4), __LINE__, notify);
182 }
183
184 // Unaligned store
185 StoreUnaligned(v4, lanes + n / 2);
186 size_t i;
187 for (i = 0; i < 3 * n / 2; ++i) {
188 NotifyIfUnequal(lanes[i], T(4), __LINE__, notify);
189 }
190 // Subsequent values remain unchanged.
191 for (; i < 2 * n; ++i) {
192 NotifyIfUnequal(lanes[i], T(5), __LINE__, notify);
193 }
194 }
195
TestAll(const HHNotify notify)196 void TestAll(const HHNotify notify) {
197 TestMembersAndBinaryOperatorsExceptShifts<uint8_t>(notify);
198 TestMembersAndBinaryOperatorsExceptShifts<uint16_t>(notify);
199 TestMembersAndBinaryOperatorsExceptShifts<uint32_t>(notify);
200 TestMembersAndBinaryOperatorsExceptShifts<uint64_t>(notify);
201
202 TestShifts<uint16_t>(notify);
203 TestShifts<uint32_t>(notify);
204 TestShifts<uint64_t>(notify);
205
206 TestLoadStore<uint8_t>(notify);
207 TestLoadStore<uint16_t>(notify);
208 TestLoadStore<uint32_t>(notify);
209 TestLoadStore<uint64_t>(notify);
210 }
211
212 } // namespace
213 } // namespace HH_TARGET_NAME
214
215 template <TargetBits Target>
operator ()(const HHNotify notify) const216 void VectorTest<Target>::operator()(const HHNotify notify) const {
217 HH_TARGET_NAME::TestAll(notify);
218 }
219
220 // Instantiate for the current target.
221 template struct VectorTest<HH_TARGET>;
222
223 } // namespace highwayhash
224 #endif // HH_DISABLE_TARGET_SPECIFIC
225