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