summaryrefslogtreecommitdiffstats
path: root/third_party/highway/hwy/tests/shift_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/highway/hwy/tests/shift_test.cc')
-rw-r--r--third_party/highway/hwy/tests/shift_test.cc428
1 files changed, 428 insertions, 0 deletions
diff --git a/third_party/highway/hwy/tests/shift_test.cc b/third_party/highway/hwy/tests/shift_test.cc
new file mode 100644
index 0000000000..585eba761c
--- /dev/null
+++ b/third_party/highway/hwy/tests/shift_test.cc
@@ -0,0 +1,428 @@
+// Copyright 2019 Google LLC
+// SPDX-License-Identifier: Apache-2.0
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <limits>
+
+#undef HWY_TARGET_INCLUDE
+#define HWY_TARGET_INCLUDE "tests/shift_test.cc"
+#include "hwy/foreach_target.h" // IWYU pragma: keep
+#include "hwy/highway.h"
+#include "hwy/tests/test_util-inl.h"
+
+HWY_BEFORE_NAMESPACE();
+namespace hwy {
+namespace HWY_NAMESPACE {
+
+template <bool kSigned>
+struct TestLeftShifts {
+ template <typename T, class D>
+ HWY_NOINLINE void operator()(T t, D d) {
+ if (kSigned) {
+ // Also test positive values
+ TestLeftShifts</*kSigned=*/false>()(t, d);
+ }
+
+ using TI = MakeSigned<T>;
+ using TU = MakeUnsigned<T>;
+ const size_t N = Lanes(d);
+ auto expected = AllocateAligned<T>(N);
+
+ // Values to shift
+ const auto values = Iota(d, static_cast<T>(kSigned ? -TI(N) : TI(0)));
+ constexpr size_t kMaxShift = (sizeof(T) * 8) - 1;
+
+ // 0
+ HWY_ASSERT_VEC_EQ(d, values, ShiftLeft<0>(values));
+ HWY_ASSERT_VEC_EQ(d, values, ShiftLeftSame(values, 0));
+
+ // 1
+ for (size_t i = 0; i < N; ++i) {
+ const T value = kSigned ? T(T(i) - T(N)) : T(i);
+ expected[i] = T(TU(value) << 1);
+ }
+ HWY_ASSERT_VEC_EQ(d, expected.get(), ShiftLeft<1>(values));
+ HWY_ASSERT_VEC_EQ(d, expected.get(), ShiftLeftSame(values, 1));
+
+ // max
+ for (size_t i = 0; i < N; ++i) {
+ const T value = kSigned ? T(T(i) - T(N)) : T(i);
+ expected[i] = T(TU(value) << kMaxShift);
+ }
+ HWY_ASSERT_VEC_EQ(d, expected.get(), ShiftLeft<kMaxShift>(values));
+ HWY_ASSERT_VEC_EQ(d, expected.get(), ShiftLeftSame(values, kMaxShift));
+ }
+};
+
+template <bool kSigned>
+struct TestVariableLeftShifts {
+ template <typename T, class D>
+ HWY_NOINLINE void operator()(T t, D d) {
+ if (kSigned) {
+ // Also test positive values
+ TestVariableLeftShifts</*kSigned=*/false>()(t, d);
+ }
+
+ using TI = MakeSigned<T>;
+ using TU = MakeUnsigned<T>;
+ const size_t N = Lanes(d);
+ auto expected = AllocateAligned<T>(N);
+
+ const auto v0 = Zero(d);
+ const auto v1 = Set(d, 1);
+ const auto values = Iota(d, kSigned ? -TI(N) : TI(0)); // value to shift
+
+ constexpr size_t kMaxShift = (sizeof(T) * 8) - 1;
+ const auto max_shift = Set(d, kMaxShift);
+ const auto small_shifts = And(Iota(d, 0), max_shift);
+ const auto large_shifts = max_shift - small_shifts;
+
+ // Same: 0
+ HWY_ASSERT_VEC_EQ(d, values, Shl(values, v0));
+
+ // Same: 1
+ for (size_t i = 0; i < N; ++i) {
+ const T value = kSigned ? T(i) - T(N) : T(i);
+ expected[i] = T(TU(value) << 1);
+ }
+ HWY_ASSERT_VEC_EQ(d, expected.get(), Shl(values, v1));
+
+ // Same: max
+ for (size_t i = 0; i < N; ++i) {
+ const T value = kSigned ? T(i) - T(N) : T(i);
+ expected[i] = T(TU(value) << kMaxShift);
+ }
+ HWY_ASSERT_VEC_EQ(d, expected.get(), Shl(values, max_shift));
+
+ // Variable: small
+ for (size_t i = 0; i < N; ++i) {
+ const T value = kSigned ? T(i) - T(N) : T(i);
+ expected[i] = T(TU(value) << (i & kMaxShift));
+ }
+ HWY_ASSERT_VEC_EQ(d, expected.get(), Shl(values, small_shifts));
+
+ // Variable: large
+ for (size_t i = 0; i < N; ++i) {
+ expected[i] = T(TU(1) << (kMaxShift - (i & kMaxShift)));
+ }
+ HWY_ASSERT_VEC_EQ(d, expected.get(), Shl(v1, large_shifts));
+ }
+};
+
+struct TestUnsignedRightShifts {
+ template <typename T, class D>
+ HWY_NOINLINE void operator()(T /*unused*/, D d) {
+ const size_t N = Lanes(d);
+ auto expected = AllocateAligned<T>(N);
+
+ const auto values = Iota(d, 0);
+
+ const T kMax = LimitsMax<T>();
+ constexpr size_t kMaxShift = (sizeof(T) * 8) - 1;
+
+ // Shift by 0
+ HWY_ASSERT_VEC_EQ(d, values, ShiftRight<0>(values));
+ HWY_ASSERT_VEC_EQ(d, values, ShiftRightSame(values, 0));
+
+ // Shift by 1
+ for (size_t i = 0; i < N; ++i) {
+ expected[i] = T(T(i & kMax) >> 1);
+ }
+ HWY_ASSERT_VEC_EQ(d, expected.get(), ShiftRight<1>(values));
+ HWY_ASSERT_VEC_EQ(d, expected.get(), ShiftRightSame(values, 1));
+
+ // max
+ for (size_t i = 0; i < N; ++i) {
+ expected[i] = T(T(i & kMax) >> kMaxShift);
+ }
+ HWY_ASSERT_VEC_EQ(d, expected.get(), ShiftRight<kMaxShift>(values));
+ HWY_ASSERT_VEC_EQ(d, expected.get(), ShiftRightSame(values, kMaxShift));
+ }
+};
+
+struct TestRotateRight {
+ template <typename T, class D>
+ HWY_NOINLINE void operator()(T /*unused*/, D d) {
+ const size_t N = Lanes(d);
+ auto expected = AllocateAligned<T>(N);
+
+ constexpr size_t kBits = sizeof(T) * 8;
+ const auto mask_shift = Set(d, T{kBits});
+ // Cover as many bit positions as possible to test shifting out
+ const auto values = Shl(Set(d, T{1}), And(Iota(d, 0), mask_shift));
+
+ // Rotate by 0
+ HWY_ASSERT_VEC_EQ(d, values, RotateRight<0>(values));
+
+ // Rotate by 1
+ Store(values, d, expected.get());
+ for (size_t i = 0; i < N; ++i) {
+ expected[i] = (expected[i] >> 1) | (expected[i] << (kBits - 1));
+ }
+ HWY_ASSERT_VEC_EQ(d, expected.get(), RotateRight<1>(values));
+
+ // Rotate by half
+ Store(values, d, expected.get());
+ for (size_t i = 0; i < N; ++i) {
+ expected[i] = (expected[i] >> (kBits / 2)) | (expected[i] << (kBits / 2));
+ }
+ HWY_ASSERT_VEC_EQ(d, expected.get(), RotateRight<kBits / 2>(values));
+
+ // Rotate by max
+ Store(values, d, expected.get());
+ for (size_t i = 0; i < N; ++i) {
+ expected[i] = (expected[i] >> (kBits - 1)) | (expected[i] << 1);
+ }
+ HWY_ASSERT_VEC_EQ(d, expected.get(), RotateRight<kBits - 1>(values));
+ }
+};
+
+struct TestVariableUnsignedRightShifts {
+ template <typename T, class D>
+ HWY_NOINLINE void operator()(T /*unused*/, D d) {
+ const size_t N = Lanes(d);
+ auto expected = AllocateAligned<T>(N);
+
+ const auto v0 = Zero(d);
+ const auto v1 = Set(d, 1);
+ const auto values = Iota(d, 0);
+
+ const T kMax = LimitsMax<T>();
+ const auto max = Set(d, kMax);
+
+ constexpr size_t kMaxShift = (sizeof(T) * 8) - 1;
+ const auto max_shift = Set(d, kMaxShift);
+ const auto small_shifts = And(Iota(d, 0), max_shift);
+ const auto large_shifts = max_shift - small_shifts;
+
+ // Same: 0
+ HWY_ASSERT_VEC_EQ(d, values, Shr(values, v0));
+
+ // Same: 1
+ for (size_t i = 0; i < N; ++i) {
+ expected[i] = T(T(i & kMax) >> 1);
+ }
+ HWY_ASSERT_VEC_EQ(d, expected.get(), Shr(values, v1));
+
+ // Same: max
+ HWY_ASSERT_VEC_EQ(d, v0, Shr(values, max_shift));
+
+ // Variable: small
+ for (size_t i = 0; i < N; ++i) {
+ expected[i] = T(i) >> (i & kMaxShift);
+ }
+ HWY_ASSERT_VEC_EQ(d, expected.get(), Shr(values, small_shifts));
+
+ // Variable: Large
+ for (size_t i = 0; i < N; ++i) {
+ expected[i] = kMax >> (kMaxShift - (i & kMaxShift));
+ }
+ HWY_ASSERT_VEC_EQ(d, expected.get(), Shr(max, large_shifts));
+ }
+};
+
+template <int kAmount, typename T>
+T RightShiftNegative(T val) {
+ // C++ shifts are implementation-defined for negative numbers, and we have
+ // seen divisions replaced with shifts, so resort to bit operations.
+ using TU = hwy::MakeUnsigned<T>;
+ TU bits;
+ CopySameSize(&val, &bits);
+
+ const TU shifted = TU(bits >> kAmount);
+
+ const TU all = TU(~TU(0));
+ const size_t num_zero = sizeof(TU) * 8 - 1 - kAmount;
+ const TU sign_extended = static_cast<TU>((all << num_zero) & LimitsMax<TU>());
+
+ bits = shifted | sign_extended;
+ CopySameSize(&bits, &val);
+ return val;
+}
+
+class TestSignedRightShifts {
+ public:
+ template <typename T, class D>
+ HWY_NOINLINE void operator()(T /*unused*/, D d) {
+ const size_t N = Lanes(d);
+ auto expected = AllocateAligned<T>(N);
+ constexpr T kMin = LimitsMin<T>();
+ constexpr T kMax = LimitsMax<T>();
+ constexpr size_t kMaxShift = (sizeof(T) * 8) - 1;
+
+ // First test positive values, negative are checked below.
+ const auto v0 = Zero(d);
+ const auto values = And(Iota(d, 0), Set(d, kMax));
+
+ // Shift by 0
+ HWY_ASSERT_VEC_EQ(d, values, ShiftRight<0>(values));
+ HWY_ASSERT_VEC_EQ(d, values, ShiftRightSame(values, 0));
+
+ // Shift by 1
+ for (size_t i = 0; i < N; ++i) {
+ expected[i] = T(T(i & kMax) >> 1);
+ }
+ HWY_ASSERT_VEC_EQ(d, expected.get(), ShiftRight<1>(values));
+ HWY_ASSERT_VEC_EQ(d, expected.get(), ShiftRightSame(values, 1));
+
+ // max
+ HWY_ASSERT_VEC_EQ(d, v0, ShiftRight<kMaxShift>(values));
+ HWY_ASSERT_VEC_EQ(d, v0, ShiftRightSame(values, kMaxShift));
+
+ // Even negative value
+ Test<0>(kMin, d, __LINE__);
+ Test<1>(kMin, d, __LINE__);
+ Test<2>(kMin, d, __LINE__);
+ Test<kMaxShift>(kMin, d, __LINE__);
+
+ const T odd = static_cast<T>(kMin + 1);
+ Test<0>(odd, d, __LINE__);
+ Test<1>(odd, d, __LINE__);
+ Test<2>(odd, d, __LINE__);
+ Test<kMaxShift>(odd, d, __LINE__);
+ }
+
+ private:
+ template <int kAmount, typename T, class D>
+ void Test(T val, D d, int line) {
+ const auto expected = Set(d, RightShiftNegative<kAmount>(val));
+ const auto in = Set(d, val);
+ const char* file = __FILE__;
+ AssertVecEqual(d, expected, ShiftRight<kAmount>(in), file, line);
+ AssertVecEqual(d, expected, ShiftRightSame(in, kAmount), file, line);
+ }
+};
+
+struct TestVariableSignedRightShifts {
+ template <typename T, class D>
+ HWY_NOINLINE void operator()(T /*unused*/, D d) {
+ using TU = MakeUnsigned<T>;
+ const size_t N = Lanes(d);
+ auto expected = AllocateAligned<T>(N);
+
+ constexpr T kMin = LimitsMin<T>();
+ constexpr T kMax = LimitsMax<T>();
+
+ constexpr size_t kMaxShift = (sizeof(T) * 8) - 1;
+
+ // First test positive values, negative are checked below.
+ const auto v0 = Zero(d);
+ const auto positive = Iota(d, 0) & Set(d, kMax);
+
+ // Shift by 0
+ HWY_ASSERT_VEC_EQ(d, positive, ShiftRight<0>(positive));
+ HWY_ASSERT_VEC_EQ(d, positive, ShiftRightSame(positive, 0));
+
+ // Shift by 1
+ for (size_t i = 0; i < N; ++i) {
+ expected[i] = T(T(i & kMax) >> 1);
+ }
+ HWY_ASSERT_VEC_EQ(d, expected.get(), ShiftRight<1>(positive));
+ HWY_ASSERT_VEC_EQ(d, expected.get(), ShiftRightSame(positive, 1));
+
+ // max
+ HWY_ASSERT_VEC_EQ(d, v0, ShiftRight<kMaxShift>(positive));
+ HWY_ASSERT_VEC_EQ(d, v0, ShiftRightSame(positive, kMaxShift));
+
+ const auto max_shift = Set(d, kMaxShift);
+ const auto small_shifts = And(Iota(d, 0), max_shift);
+ const auto large_shifts = max_shift - small_shifts;
+
+ const auto negative = Iota(d, kMin);
+
+ // Test varying negative to shift
+ for (size_t i = 0; i < N; ++i) {
+ expected[i] = RightShiftNegative<1>(static_cast<T>(kMin + i));
+ }
+ HWY_ASSERT_VEC_EQ(d, expected.get(), Shr(negative, Set(d, 1)));
+
+ // Shift MSB right by small amounts
+ for (size_t i = 0; i < N; ++i) {
+ const size_t amount = i & kMaxShift;
+ const TU shifted = ~((1ull << (kMaxShift - amount)) - 1);
+ CopySameSize(&shifted, &expected[i]);
+ }
+ HWY_ASSERT_VEC_EQ(d, expected.get(), Shr(Set(d, kMin), small_shifts));
+
+ // Shift MSB right by large amounts
+ for (size_t i = 0; i < N; ++i) {
+ const size_t amount = kMaxShift - (i & kMaxShift);
+ const TU shifted = ~((1ull << (kMaxShift - amount)) - 1);
+ CopySameSize(&shifted, &expected[i]);
+ }
+ HWY_ASSERT_VEC_EQ(d, expected.get(), Shr(Set(d, kMin), large_shifts));
+ }
+};
+
+HWY_NOINLINE void TestAllShifts() {
+ ForUnsignedTypes(ForPartialVectors<TestLeftShifts</*kSigned=*/false>>());
+ ForSignedTypes(ForPartialVectors<TestLeftShifts</*kSigned=*/true>>());
+ ForUnsignedTypes(ForPartialVectors<TestUnsignedRightShifts>());
+ ForSignedTypes(ForPartialVectors<TestSignedRightShifts>());
+}
+
+HWY_NOINLINE void TestAllVariableShifts() {
+ const ForPartialVectors<TestLeftShifts</*kSigned=*/false>> shl_u;
+ const ForPartialVectors<TestLeftShifts</*kSigned=*/true>> shl_s;
+ const ForPartialVectors<TestUnsignedRightShifts> shr_u;
+ const ForPartialVectors<TestSignedRightShifts> shr_s;
+
+ shl_u(uint16_t());
+ shr_u(uint16_t());
+
+ shl_u(uint32_t());
+ shr_u(uint32_t());
+
+ shl_s(int16_t());
+ shr_s(int16_t());
+
+ shl_s(int32_t());
+ shr_s(int32_t());
+
+#if HWY_HAVE_INTEGER64
+ shl_u(uint64_t());
+ shr_u(uint64_t());
+
+ shl_s(int64_t());
+ shr_s(int64_t());
+#endif
+}
+
+HWY_NOINLINE void TestAllRotateRight() {
+ const ForPartialVectors<TestRotateRight> test;
+ test(uint32_t());
+#if HWY_HAVE_INTEGER64
+ test(uint64_t());
+#endif
+}
+
+// NOLINTNEXTLINE(google-readability-namespace-comments)
+} // namespace HWY_NAMESPACE
+} // namespace hwy
+HWY_AFTER_NAMESPACE();
+
+#if HWY_ONCE
+
+namespace hwy {
+HWY_BEFORE_TEST(HwyShiftTest);
+HWY_EXPORT_AND_TEST_P(HwyShiftTest, TestAllShifts);
+HWY_EXPORT_AND_TEST_P(HwyShiftTest, TestAllVariableShifts);
+HWY_EXPORT_AND_TEST_P(HwyShiftTest, TestAllRotateRight);
+} // namespace hwy
+
+#endif