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