1 /* 2 * Copyright (c) 2023 The WebM project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "./vpx_config.h" 12 #include "arm_cpudetect.h" 13 14 #if defined(__APPLE__) 15 #include <sys/sysctl.h> 16 #endif 17 18 #if !CONFIG_RUNTIME_CPU_DETECT 19 arm_get_cpu_caps(void)20static int arm_get_cpu_caps(void) { 21 // This function should actually be a no-op. There is no way to adjust any of 22 // these because the RTCD tables do not exist: the functions are called 23 // statically. 24 int flags = 0; 25 #if HAVE_NEON 26 flags |= HAS_NEON; 27 #endif // HAVE_NEON 28 return flags; 29 } 30 31 #elif defined(__APPLE__) // end !CONFIG_RUNTIME_CPU_DETECT 32 33 // sysctlbyname() parameter documentation for instruction set characteristics: 34 // https://developer.apple.com/documentation/kernel/1387446-sysctlbyname/determining_instruction_set_characteristics have_feature(const char * feature)35static INLINE int64_t have_feature(const char *feature) { 36 int64_t feature_present = 0; 37 size_t size = sizeof(feature_present); 38 if (sysctlbyname(feature, &feature_present, &size, NULL, 0) != 0) { 39 return 0; 40 } 41 return feature_present; 42 } 43 arm_get_cpu_caps(void)44static int arm_get_cpu_caps(void) { 45 int flags = 0; 46 #if HAVE_NEON 47 flags |= HAS_NEON; 48 #endif // HAVE_NEON 49 #if HAVE_NEON_DOTPROD 50 if (have_feature("hw.optional.arm.FEAT_DotProd")) { 51 flags |= HAS_NEON_DOTPROD; 52 } 53 #endif // HAVE_NEON_DOTPROD 54 #if HAVE_NEON_I8MM 55 if (have_feature("hw.optional.arm.FEAT_I8MM")) { 56 flags |= HAS_NEON_I8MM; 57 } 58 #endif // HAVE_NEON_I8MM 59 return flags; 60 } 61 62 #elif defined(_WIN32) // end __APPLE__ 63 arm_get_cpu_caps(void)64static int arm_get_cpu_caps(void) { 65 int flags = 0; 66 // IsProcessorFeaturePresent() parameter documentation: 67 // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-isprocessorfeaturepresent#parameters 68 #if HAVE_NEON 69 flags |= HAS_NEON; // Neon is mandatory in Armv8.0-A. 70 #endif // HAVE_NEON 71 #if HAVE_NEON_DOTPROD 72 // Support for PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE was added in Windows SDK 73 // 20348, supported by Windows 11 and Windows Server 2022. 74 #if defined(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE) 75 if (IsProcessorFeaturePresent(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE)) { 76 flags |= HAS_NEON_DOTPROD; 77 } 78 #endif // defined(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE) 79 #endif // HAVE_NEON_DOTPROD 80 // No I8MM or SVE feature detection available on Windows at time of writing. 81 return flags; 82 } 83 84 #elif defined(ANDROID_USE_CPU_FEATURES_LIB) 85 arm_get_cpu_caps(void)86static int arm_get_cpu_caps(void) { 87 int flags = 0; 88 #if HAVE_NEON 89 flags |= HAS_NEON; // Neon is mandatory in Armv8.0-A. 90 #endif // HAVE_NEON 91 return flags; 92 } 93 94 #elif defined(__linux__) // end defined(VPX_USE_ANDROID_CPU_FEATURES) 95 96 #include <sys/auxv.h> 97 98 // Define hwcap values ourselves: building with an old auxv header where these 99 // hwcap values are not defined should not prevent features from being enabled. 100 #define VPX_AARCH64_HWCAP_ASIMDDP (1 << 20) 101 #define VPX_AARCH64_HWCAP_SVE (1 << 22) 102 #define VPX_AARCH64_HWCAP2_I8MM (1 << 13) 103 arm_get_cpu_caps(void)104static int arm_get_cpu_caps(void) { 105 int flags = 0; 106 unsigned long hwcap = getauxval(AT_HWCAP); 107 #if HAVE_NEON_I8MM 108 unsigned long hwcap2 = getauxval(AT_HWCAP2); 109 #endif // HAVE_NEON_I8MM 110 #if HAVE_NEON 111 flags |= HAS_NEON; // Neon is mandatory in Armv8.0-A. 112 #endif // HAVE_NEON 113 #if HAVE_NEON_DOTPROD 114 if (hwcap & VPX_AARCH64_HWCAP_ASIMDDP) { 115 flags |= HAS_NEON_DOTPROD; 116 } 117 #endif // HAVE_NEON_DOTPROD 118 #if HAVE_NEON_I8MM 119 if (hwcap2 & VPX_AARCH64_HWCAP2_I8MM) { 120 flags |= HAS_NEON_I8MM; 121 } 122 #endif // HAVE_NEON_I8MM 123 #if HAVE_SVE 124 if (hwcap & VPX_AARCH64_HWCAP_SVE) { 125 flags |= HAS_SVE; 126 } 127 #endif // HAVE_SVE 128 return flags; 129 } 130 131 #elif defined(__Fuchsia__) // end __linux__ 132 133 #include <zircon/features.h> 134 #include <zircon/syscalls.h> 135 136 // Added in https://fuchsia-review.googlesource.com/c/fuchsia/+/894282. 137 #ifndef ZX_ARM64_FEATURE_ISA_I8MM 138 #define ZX_ARM64_FEATURE_ISA_I8MM ((uint32_t)(1u << 19)) 139 #endif 140 // Added in https://fuchsia-review.googlesource.com/c/fuchsia/+/895083. 141 #ifndef ZX_ARM64_FEATURE_ISA_SVE 142 #define ZX_ARM64_FEATURE_ISA_SVE ((uint32_t)(1u << 20)) 143 #endif 144 arm_get_cpu_caps(void)145static int arm_get_cpu_caps(void) { 146 int flags = 0; 147 #if HAVE_NEON 148 flags |= HAS_NEON; // Neon is mandatory in Armv8.0-A. 149 #endif // HAVE_NEON 150 uint32_t features; 151 zx_status_t status = zx_system_get_features(ZX_FEATURE_KIND_CPU, &features); 152 if (status != ZX_OK) { 153 return flags; 154 } 155 #if HAVE_NEON_DOTPROD 156 if (features & ZX_ARM64_FEATURE_ISA_DP) { 157 flags |= HAS_NEON_DOTPROD; 158 } 159 #endif // HAVE_NEON_DOTPROD 160 #if HAVE_NEON_I8MM 161 if (features & ZX_ARM64_FEATURE_ISA_I8MM) { 162 flags |= HAS_NEON_I8MM; 163 } 164 #endif // HAVE_NEON_I8MM 165 #if HAVE_SVE 166 if (features & ZX_ARM64_FEATURE_ISA_SVE) { 167 flags |= HAS_SVE; 168 } 169 #endif // HAVE_SVE 170 return flags; 171 } 172 173 #else // end __Fuchsia__ 174 #error \ 175 "Runtime CPU detection selected, but no CPU detection method available" \ 176 "for your platform. Rerun configure with --disable-runtime-cpu-detect." 177 #endif 178 arm_cpu_caps(void)179int arm_cpu_caps(void) { 180 int flags = 0; 181 if (!arm_cpu_env_flags(&flags)) { 182 flags = arm_get_cpu_caps() & arm_cpu_env_mask(); 183 } 184 185 // Restrict flags: FEAT_I8MM assumes that FEAT_DotProd is available. 186 if (!(flags & HAS_NEON_DOTPROD)) { 187 flags &= ~HAS_NEON_I8MM; 188 } 189 190 // Restrict flags: FEAT_SVE assumes that FEAT_{DotProd,I8MM} are available. 191 if (!(flags & HAS_NEON_DOTPROD)) { 192 flags &= ~HAS_SVE; 193 } 194 if (!(flags & HAS_NEON_I8MM)) { 195 flags &= ~HAS_SVE; 196 } 197 198 return flags; 199 } 200