targets_test.cc (6760B)
1 // Copyright 2020 Google LLC 2 // SPDX-License-Identifier: Apache-2.0 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 #include "hwy/targets.h" 17 18 #include <stdint.h> 19 20 #include "hwy/detect_targets.h" 21 #include "hwy/tests/hwy_gtest.h" 22 #include "hwy/tests/test_util-inl.h" 23 24 // Simulate another project having its own namespace. 25 namespace fake { 26 namespace { 27 28 #define DECLARE_FUNCTION(TGT) \ 29 namespace N_##TGT { \ 30 /* Function argument is just to ensure/demonstrate they are possible. */ \ 31 HWY_MAYBE_UNUSED int64_t FakeFunction(int) { return HWY_##TGT; } \ 32 template <typename T> \ 33 int64_t FakeFunctionT(T) { \ 34 return HWY_##TGT; \ 35 } \ 36 } 37 38 DECLARE_FUNCTION(AVX10_2) 39 DECLARE_FUNCTION(AVX3_SPR) 40 DECLARE_FUNCTION(AVX3_ZEN4) 41 DECLARE_FUNCTION(AVX3_DL) 42 DECLARE_FUNCTION(AVX3) 43 DECLARE_FUNCTION(AVX2) 44 DECLARE_FUNCTION(SSE4) 45 DECLARE_FUNCTION(SSSE3) 46 DECLARE_FUNCTION(SSE2) 47 48 DECLARE_FUNCTION(SVE2_128) 49 DECLARE_FUNCTION(SVE_256) 50 DECLARE_FUNCTION(SVE2) 51 DECLARE_FUNCTION(SVE) 52 DECLARE_FUNCTION(NEON_BF16) 53 DECLARE_FUNCTION(NEON) 54 DECLARE_FUNCTION(NEON_WITHOUT_AES) 55 56 DECLARE_FUNCTION(PPC10) 57 DECLARE_FUNCTION(PPC9) 58 DECLARE_FUNCTION(PPC8) 59 60 DECLARE_FUNCTION(Z15) 61 DECLARE_FUNCTION(Z14) 62 63 DECLARE_FUNCTION(WASM) 64 DECLARE_FUNCTION(WASM_EMU256) 65 66 DECLARE_FUNCTION(RVV) 67 68 DECLARE_FUNCTION(LASX) 69 DECLARE_FUNCTION(LSX) 70 71 DECLARE_FUNCTION(SCALAR) 72 DECLARE_FUNCTION(EMU128) 73 74 HWY_EXPORT(FakeFunction); 75 76 template <typename T> 77 int64_t FakeFunctionDispatcher(T value) { 78 // Note that when calling templated code on arbitrary types, the dispatch 79 // table must be defined inside another template function. 80 HWY_EXPORT_T(FakeFunction1, FakeFunctionT<T>); 81 HWY_EXPORT_T(FakeFunction2, FakeFunctionT<bool>); 82 // Verify two EXPORT_T within a function are possible. 83 return hwy::AddWithWraparound(HWY_DYNAMIC_DISPATCH_T(FakeFunction1)(value), 84 HWY_DYNAMIC_DISPATCH_T(FakeFunction2)(true)); 85 } 86 87 void CallFunctionForTarget(int64_t target, int /*line*/) { 88 if ((HWY_TARGETS & target) == 0) return; 89 hwy::SetSupportedTargetsForTest(target); 90 91 // Call Update() first to make &HWY_DYNAMIC_DISPATCH() return 92 // the pointer to the already cached function. 93 hwy::GetChosenTarget().Update(hwy::SupportedTargets()); 94 95 HWY_ASSERT_EQ(target, HWY_DYNAMIC_DISPATCH(FakeFunction)(42)); 96 97 // * 2 because we call two functions and add their target result together. 98 const int64_t target_times_2 = 99 static_cast<int64_t>(static_cast<uint64_t>(target) * 2ULL); 100 HWY_ASSERT_EQ(target_times_2, FakeFunctionDispatcher<float>(1.0f)); 101 HWY_ASSERT_EQ(target_times_2, FakeFunctionDispatcher<double>(1.0)); 102 103 // Calling DeInit() will test that the initializer function 104 // also calls the right function. 105 hwy::GetChosenTarget().DeInit(); 106 107 const int64_t expected = target; 108 HWY_ASSERT_EQ(expected, HWY_DYNAMIC_DISPATCH(FakeFunction)(42)); 109 110 // Second call uses the cached value from the previous call. 111 HWY_ASSERT_EQ(target, HWY_DYNAMIC_DISPATCH(FakeFunction)(42)); 112 } 113 114 void CheckFakeFunction() { 115 // When adding a target, also add to DECLARE_FUNCTION above. 116 CallFunctionForTarget(HWY_AVX3_SPR, __LINE__); 117 CallFunctionForTarget(HWY_AVX3_ZEN4, __LINE__); 118 CallFunctionForTarget(HWY_AVX3_DL, __LINE__); 119 CallFunctionForTarget(HWY_AVX3, __LINE__); 120 CallFunctionForTarget(HWY_AVX2, __LINE__); 121 CallFunctionForTarget(HWY_SSE4, __LINE__); 122 CallFunctionForTarget(HWY_SSSE3, __LINE__); 123 CallFunctionForTarget(HWY_SSE2, __LINE__); 124 125 CallFunctionForTarget(HWY_SVE2_128, __LINE__); 126 CallFunctionForTarget(HWY_SVE_256, __LINE__); 127 CallFunctionForTarget(HWY_SVE2, __LINE__); 128 CallFunctionForTarget(HWY_SVE, __LINE__); 129 CallFunctionForTarget(HWY_NEON_BF16, __LINE__); 130 CallFunctionForTarget(HWY_NEON, __LINE__); 131 CallFunctionForTarget(HWY_NEON_WITHOUT_AES, __LINE__); 132 133 CallFunctionForTarget(HWY_PPC10, __LINE__); 134 CallFunctionForTarget(HWY_PPC9, __LINE__); 135 CallFunctionForTarget(HWY_PPC8, __LINE__); 136 137 CallFunctionForTarget(HWY_WASM, __LINE__); 138 CallFunctionForTarget(HWY_WASM_EMU256, __LINE__); 139 140 CallFunctionForTarget(HWY_RVV, __LINE__); 141 142 CallFunctionForTarget(HWY_LASX, __LINE__); 143 CallFunctionForTarget(HWY_LSX, __LINE__); 144 145 // The tables only have space for either HWY_SCALAR or HWY_EMU128; the former 146 // is opt-in only. 147 #if defined(HWY_COMPILE_ONLY_SCALAR) || HWY_BROKEN_EMU128 148 CallFunctionForTarget(HWY_SCALAR, __LINE__); 149 #else 150 CallFunctionForTarget(HWY_EMU128, __LINE__); 151 #endif 152 } 153 154 } // namespace 155 } // namespace fake 156 157 namespace hwy { 158 namespace { 159 160 #if !HWY_TEST_STANDALONE 161 class HwyTargetsTest : public testing::Test {}; 162 #endif 163 164 // Test that the order in the HWY_EXPORT static array matches the expected 165 // value of the target bits. This is only checked for the targets that are 166 // enabled in the current compilation. 167 TEST(HwyTargetsTest, ChosenTargetOrderTest) { fake::CheckFakeFunction(); } 168 169 TEST(HwyTargetsTest, DisabledTargetsTest) { 170 SetSupportedTargetsForTest(0); 171 DisableTargets(~0LL); 172 // Check that disabling everything at least leaves the static target. 173 HWY_ASSERT(HWY_STATIC_TARGET == SupportedTargets()); 174 175 DisableTargets(0); // Reset the mask. 176 const int64_t current_targets = SupportedTargets(); 177 const int64_t enabled_baseline = static_cast<int64_t>(HWY_ENABLED_BASELINE); 178 // Exclude these two because they are always returned by SupportedTargets. 179 const int64_t fallback = HWY_SCALAR | HWY_EMU128; 180 if ((current_targets & ~enabled_baseline & ~fallback) == 0) { 181 // We can't test anything else if the only compiled target is the baseline. 182 return; 183 } 184 185 // Get the lowest bit in the mask (the best target) and disable that one. 186 const int64_t best_target = current_targets & (~current_targets + 1); 187 DisableTargets(best_target); 188 189 // Check that the other targets are still enabled. 190 HWY_ASSERT((best_target ^ current_targets) == SupportedTargets()); 191 DisableTargets(0); // Reset the mask. 192 } 193 194 } // namespace 195 } // namespace hwy 196 197 HWY_TEST_MAIN();