1 // Copyright 2019 Google LLC
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 #include <stddef.h>
16 #include <stdint.h>
17 
18 #undef HWY_TARGET_INCLUDE
19 #define HWY_TARGET_INCLUDE "tests/combine_test.cc"
20 #include "hwy/foreach_target.h"
21 
22 #include "hwy/highway.h"
23 #include "hwy/tests/test_util-inl.h"
24 
25 // Not yet implemented
26 #if HWY_TARGET != HWY_RVV
27 
28 HWY_BEFORE_NAMESPACE();
29 namespace hwy {
30 namespace HWY_NAMESPACE {
31 
32 struct TestLowerHalf {
33   template <class T, class D>
operator ()hwy::HWY_NAMESPACE::TestLowerHalf34   HWY_NOINLINE void operator()(T /*unused*/, D d) {
35     const Half<D> d2;
36 
37     const size_t N = Lanes(d);
38     auto lanes = AllocateAligned<T>(N);
39     auto lanes2 = AllocateAligned<T>(N);
40     std::fill(lanes.get(), lanes.get() + N, T(0));
41     std::fill(lanes2.get(), lanes2.get() + N, T(0));
42     const auto v = Iota(d, 1);
43     Store(LowerHalf(d2, v), d2, lanes.get());
44     Store(LowerHalf(v), d2, lanes2.get());  // optionally without D
45     size_t i = 0;
46     for (; i < Lanes(d2); ++i) {
47       HWY_ASSERT_EQ(T(1 + i), lanes[i]);
48       HWY_ASSERT_EQ(T(1 + i), lanes2[i]);
49     }
50     // Other half remains unchanged
51     for (; i < N; ++i) {
52       HWY_ASSERT_EQ(T(0), lanes[i]);
53       HWY_ASSERT_EQ(T(0), lanes2[i]);
54     }
55   }
56 };
57 
58 struct TestLowerQuarter {
59   template <class T, class D>
operator ()hwy::HWY_NAMESPACE::TestLowerQuarter60   HWY_NOINLINE void operator()(T /*unused*/, D d) {
61     const Half<D> d2;
62     const Half<decltype(d2)> d4;
63 
64     const size_t N = Lanes(d);
65     auto lanes = AllocateAligned<T>(N);
66     auto lanes2 = AllocateAligned<T>(N);
67     std::fill(lanes.get(), lanes.get() + N, T(0));
68     std::fill(lanes2.get(), lanes2.get() + N, T(0));
69     const auto v = Iota(d, 1);
70     const auto lo = LowerHalf(d4, LowerHalf(d2, v));
71     const auto lo2 = LowerHalf(LowerHalf(v));  // optionally without D
72     Store(lo, d4, lanes.get());
73     Store(lo2, d4, lanes2.get());
74     size_t i = 0;
75     for (; i < Lanes(d4); ++i) {
76       HWY_ASSERT_EQ(T(i + 1), lanes[i]);
77       HWY_ASSERT_EQ(T(i + 1), lanes2[i]);
78     }
79     // Upper 3/4 remain unchanged
80     for (; i < N; ++i) {
81       HWY_ASSERT_EQ(T(0), lanes[i]);
82       HWY_ASSERT_EQ(T(0), lanes2[i]);
83     }
84   }
85 };
86 
TestAllLowerHalf()87 HWY_NOINLINE void TestAllLowerHalf() {
88   ForAllTypes(ForDemoteVectors<TestLowerHalf>());
89   ForAllTypes(ForDemoteVectors<TestLowerQuarter, 4>());
90 }
91 
92 struct TestUpperHalf {
93   template <class T, class D>
operator ()hwy::HWY_NAMESPACE::TestUpperHalf94   HWY_NOINLINE void operator()(T /*unused*/, D d) {
95     // Scalar does not define UpperHalf.
96 #if HWY_TARGET != HWY_SCALAR
97     const Half<D> d2;
98 
99     const auto v = Iota(d, 1);
100     const size_t N = Lanes(d);
101     auto lanes = AllocateAligned<T>(N);
102     std::fill(lanes.get(), lanes.get() + N, T(0));
103 
104     Store(UpperHalf(d2, v), d2, lanes.get());
105     size_t i = 0;
106     for (; i < Lanes(d2); ++i) {
107       HWY_ASSERT_EQ(T(Lanes(d2) + 1 + i), lanes[i]);
108     }
109     // Other half remains unchanged
110     for (; i < N; ++i) {
111       HWY_ASSERT_EQ(T(0), lanes[i]);
112     }
113 #else
114     (void)d;
115 #endif
116   }
117 };
118 
TestAllUpperHalf()119 HWY_NOINLINE void TestAllUpperHalf() {
120   ForAllTypes(ForShrinkableVectors<TestUpperHalf>());
121 }
122 
123 struct TestZeroExtendVector {
124   template <class T, class D>
operator ()hwy::HWY_NAMESPACE::TestZeroExtendVector125   HWY_NOINLINE void operator()(T /*unused*/, D d) {
126     const Twice<D> d2;
127 
128     const auto v = Iota(d, 1);
129     const size_t N2 = Lanes(d2);
130     auto lanes = AllocateAligned<T>(N2);
131     Store(v, d, &lanes[0]);
132     Store(v, d, &lanes[N2 / 2]);
133 
134     const auto ext = ZeroExtendVector(d2, v);
135     Store(ext, d2, lanes.get());
136 
137     size_t i = 0;
138     // Lower half is unchanged
139     for (; i < N2 / 2; ++i) {
140       HWY_ASSERT_EQ(T(1 + i), lanes[i]);
141     }
142     // Upper half is zero
143     for (; i < N2; ++i) {
144       HWY_ASSERT_EQ(T(0), lanes[i]);
145     }
146   }
147 };
148 
TestAllZeroExtendVector()149 HWY_NOINLINE void TestAllZeroExtendVector() {
150   ForAllTypes(ForExtendableVectors<TestZeroExtendVector>());
151 }
152 
153 struct TestCombine {
154   template <class T, class D>
operator ()hwy::HWY_NAMESPACE::TestCombine155   HWY_NOINLINE void operator()(T /*unused*/, D d) {
156     const Twice<D> d2;
157     const size_t N2 = Lanes(d2);
158     auto lanes = AllocateAligned<T>(N2);
159 
160     const auto lo = Iota(d, 1);
161     const auto hi = Iota(d, N2 / 2 + 1);
162     const auto combined = Combine(d2, hi, lo);
163     Store(combined, d2, lanes.get());
164 
165     const auto expected = Iota(d2, 1);
166     HWY_ASSERT_VEC_EQ(d2, expected, combined);
167   }
168 };
169 
TestAllCombine()170 HWY_NOINLINE void TestAllCombine() {
171   ForAllTypes(ForExtendableVectors<TestCombine>());
172 }
173 
174 struct TestConcat {
175   template <class T, class D>
operator ()hwy::HWY_NAMESPACE::TestConcat176   HWY_NOINLINE void operator()(T /*unused*/, D d) {
177     const size_t N = Lanes(d);
178     if (N == 1) return;
179     const size_t half_bytes = N * sizeof(T) / 2;
180 
181     auto hi = AllocateAligned<T>(N);
182     auto lo = AllocateAligned<T>(N);
183     auto expected = AllocateAligned<T>(N);
184     RandomState rng;
185     for (size_t rep = 0; rep < 10; ++rep) {
186       for (size_t i = 0; i < N; ++i) {
187         hi[i] = static_cast<T>(Random64(&rng) & 0xFF);
188         lo[i] = static_cast<T>(Random64(&rng) & 0xFF);
189       }
190 
191       {
192         memcpy(&expected[N / 2], &hi[N / 2], half_bytes);
193         memcpy(&expected[0], &lo[0], half_bytes);
194         const auto vhi = Load(d, hi.get());
195         const auto vlo = Load(d, lo.get());
196         HWY_ASSERT_VEC_EQ(d, expected.get(), ConcatUpperLower(d, vhi, vlo));
197       }
198 
199       {
200         memcpy(&expected[N / 2], &hi[N / 2], half_bytes);
201         memcpy(&expected[0], &lo[N / 2], half_bytes);
202         const auto vhi = Load(d, hi.get());
203         const auto vlo = Load(d, lo.get());
204         HWY_ASSERT_VEC_EQ(d, expected.get(), ConcatUpperUpper(d, vhi, vlo));
205       }
206 
207       {
208         memcpy(&expected[N / 2], &hi[0], half_bytes);
209         memcpy(&expected[0], &lo[N / 2], half_bytes);
210         const auto vhi = Load(d, hi.get());
211         const auto vlo = Load(d, lo.get());
212         HWY_ASSERT_VEC_EQ(d, expected.get(), ConcatLowerUpper(d, vhi, vlo));
213       }
214 
215       {
216         memcpy(&expected[N / 2], &hi[0], half_bytes);
217         memcpy(&expected[0], &lo[0], half_bytes);
218         const auto vhi = Load(d, hi.get());
219         const auto vlo = Load(d, lo.get());
220         HWY_ASSERT_VEC_EQ(d, expected.get(), ConcatLowerLower(d, vhi, vlo));
221       }
222     }
223   }
224 };
225 
TestAllConcat()226 HWY_NOINLINE void TestAllConcat() {
227   ForAllTypes(ForShrinkableVectors<TestConcat>());
228 }
229 
230 struct TestConcatOddEven {
231   template <class T, class D>
operator ()hwy::HWY_NAMESPACE::TestConcatOddEven232   HWY_NOINLINE void operator()(T /*unused*/, D d) {
233 #if HWY_TARGET != HWY_RVV && HWY_TARGET != HWY_SCALAR
234     const size_t N = Lanes(d);
235     const auto hi = Iota(d, N);
236     const auto lo = Iota(d, 0);
237     const auto even = Add(Iota(d, 0), Iota(d, 0));
238     const auto odd = Add(even, Set(d, 1));
239     HWY_ASSERT_VEC_EQ(d, odd, ConcatOdd(d, hi, lo));
240     HWY_ASSERT_VEC_EQ(d, even, ConcatEven(d, hi, lo));
241 #else
242     (void)d;
243 #endif
244   }
245 };
246 
TestAllConcatOddEven()247 HWY_NOINLINE void TestAllConcatOddEven() {
248   ForUIF3264(ForShrinkableVectors<TestConcatOddEven>());
249 }
250 
251 // NOLINTNEXTLINE(google-readability-namespace-comments)
252 }  // namespace HWY_NAMESPACE
253 }  // namespace hwy
254 HWY_AFTER_NAMESPACE();
255 
256 #if HWY_ONCE
257 
258 namespace hwy {
259 HWY_BEFORE_TEST(HwyCombineTest);
260 HWY_EXPORT_AND_TEST_P(HwyCombineTest, TestAllLowerHalf);
261 HWY_EXPORT_AND_TEST_P(HwyCombineTest, TestAllUpperHalf);
262 HWY_EXPORT_AND_TEST_P(HwyCombineTest, TestAllZeroExtendVector);
263 HWY_EXPORT_AND_TEST_P(HwyCombineTest, TestAllCombine);
264 HWY_EXPORT_AND_TEST_P(HwyCombineTest, TestAllConcat);
265 HWY_EXPORT_AND_TEST_P(HwyCombineTest, TestAllConcatOddEven);
266 }  // namespace hwy
267 
268 // Ought not to be necessary, but without this, no tests run on RVV.
main(int argc,char ** argv)269 int main(int argc, char **argv) {
270   ::testing::InitGoogleTest(&argc, argv);
271   return RUN_ALL_TESTS();
272 }
273 
274 #endif  // HWY_ONCE
275 
276 #else
main(int,char **)277 int main(int, char**) { return 0; }
278 #endif  // HWY_TARGET != HWY_RVV
279