1 /* cpu_features.c -- Processor features detection.
2  *
3  * Copyright 2018 The Chromium Authors. All rights reserved.
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the Chromium source repository LICENSE file.
6  */
7 
8 #include "cpu_features.h"
9 #include "zutil.h"
10 
11 #include <stdint.h>
12 #if defined(_MSC_VER)
13 #include <intrin.h>
14 #elif defined(ADLER32_SIMD_SSSE3)
15 #include <cpuid.h>
16 #endif
17 
18 /* TODO(cavalcantii): remove checks for x86_flags on deflate.
19  */
20 #if defined(ARMV8_OS_MACOS)
21 /* crc32 is a baseline feature in ARMv8.1-A, and macOS running on arm64 is new
22  * enough that this can be assumed without runtime detection. */
23 int ZLIB_INTERNAL arm_cpu_enable_crc32 = 1;
24 #else
25 int ZLIB_INTERNAL arm_cpu_enable_crc32 = 0;
26 #endif
27 int ZLIB_INTERNAL arm_cpu_enable_pmull = 0;
28 int ZLIB_INTERNAL x86_cpu_enable_sse2 = 0;
29 int ZLIB_INTERNAL x86_cpu_enable_ssse3 = 0;
30 int ZLIB_INTERNAL x86_cpu_enable_simd = 0;
31 
32 #ifndef CPU_NO_SIMD
33 
34 #if defined(ARMV8_OS_ANDROID) || defined(ARMV8_OS_LINUX) || defined(ARMV8_OS_FUCHSIA) || defined(ARMV8_OS_FREEBSD)
35 #include <pthread.h>
36 #endif
37 
38 #if defined(ARMV8_OS_FREEBSD)
39 #include <machine/armreg.h>
40 #include <sys/types.h>
41 #ifndef ID_AA64ISAR0_AES_VAL
42 #define ID_AA64ISAR0_AES_VAL ID_AA64ISAR0_AES
43 #endif
44 #ifndef ID_AA64ISAR0_CRC32_VAL
45 #define ID_AA64ISAR0_CRC32_VAL ID_AA64ISAR0_CRC32
46 #endif
47 #elif defined(ARMV8_OS_ANDROID)
48 #include <cpu-features.h>
49 #elif defined(ARMV8_OS_LINUX)
50 #include <asm/hwcap.h>
51 #include <sys/auxv.h>
52 #elif defined(ARMV8_OS_FUCHSIA)
53 #include <zircon/features.h>
54 #include <zircon/syscalls.h>
55 #include <zircon/types.h>
56 #elif defined(ARMV8_OS_WINDOWS) || defined(X86_WINDOWS)
57 #include <windows.h>
58 #elif !defined(_MSC_VER)
59 #include <pthread.h>
60 #else
61 #error cpu_features.c CPU feature detection in not defined for your platform
62 #endif
63 
64 #if !defined(CPU_NO_SIMD) && !defined(ARMV8_OS_MACOS) && !defined(ARM_OS_IOS)
65 static void _cpu_check_features(void);
66 #endif
67 
68 #if defined(ARMV8_OS_ANDROID) || defined(ARMV8_OS_FREEBSD) || defined(ARMV8_OS_LINUX) || defined(ARMV8_OS_MACOS) || defined(ARMV8_OS_FUCHSIA) || defined(X86_NOT_WINDOWS)
69 #if !defined(ARMV8_OS_MACOS)
70 // _cpu_check_features() doesn't need to do anything on mac/arm since all
71 // features are known at build time, so don't call it.
72 // Do provide cpu_check_features() (with a no-op implementation) so that we
73 // don't have to make all callers of it check for mac/arm.
74 static pthread_once_t cpu_check_inited_once = PTHREAD_ONCE_INIT;
75 #endif
cpu_check_features(void)76 void ZLIB_INTERNAL cpu_check_features(void)
77 {
78 #if !defined(ARMV8_OS_MACOS)
79     pthread_once(&cpu_check_inited_once, _cpu_check_features);
80 #endif
81 }
82 #elif defined(ARMV8_OS_WINDOWS) || defined(X86_WINDOWS)
83 static INIT_ONCE cpu_check_inited_once = INIT_ONCE_STATIC_INIT;
_cpu_check_features_forwarder(PINIT_ONCE once,PVOID param,PVOID * context)84 static BOOL CALLBACK _cpu_check_features_forwarder(PINIT_ONCE once, PVOID param, PVOID* context)
85 {
86     _cpu_check_features();
87     return TRUE;
88 }
cpu_check_features(void)89 void ZLIB_INTERNAL cpu_check_features(void)
90 {
91     InitOnceExecuteOnce(&cpu_check_inited_once, _cpu_check_features_forwarder,
92                         NULL, NULL);
93 }
94 #endif
95 
96 #if (defined(__ARM_NEON__) || defined(__ARM_NEON))
97 /*
98  * iOS@ARM is a special case where we always have NEON but don't check
99  * for crypto extensions.
100  */
101 #if !defined(ARMV8_OS_MACOS) && !defined(ARM_OS_IOS)
102 /*
103  * See http://bit.ly/2CcoEsr for run-time detection of ARM features and also
104  * crbug.com/931275 for android_getCpuFeatures() use in the Android sandbox.
105  */
_cpu_check_features(void)106 static void _cpu_check_features(void)
107 {
108 #if defined(ARMV8_OS_ANDROID) && defined(__aarch64__)
109     uint64_t features = android_getCpuFeatures();
110     arm_cpu_enable_crc32 = !!(features & ANDROID_CPU_ARM64_FEATURE_CRC32);
111     arm_cpu_enable_pmull = !!(features & ANDROID_CPU_ARM64_FEATURE_PMULL);
112 #elif defined(ARMV8_OS_ANDROID) /* aarch32 */
113     uint64_t features = android_getCpuFeatures();
114     arm_cpu_enable_crc32 = !!(features & ANDROID_CPU_ARM_FEATURE_CRC32);
115     arm_cpu_enable_pmull = !!(features & ANDROID_CPU_ARM_FEATURE_PMULL);
116 #elif defined(ARMV8_OS_LINUX) && defined(__aarch64__)
117     unsigned long features = getauxval(AT_HWCAP);
118     arm_cpu_enable_crc32 = !!(features & HWCAP_CRC32);
119     arm_cpu_enable_pmull = !!(features & HWCAP_PMULL);
120 #elif defined(ARMV8_OS_LINUX) && (defined(__ARM_NEON) || defined(__ARM_NEON__))
121     /* Query HWCAP2 for ARMV8-A SoCs running in aarch32 mode */
122     unsigned long features = getauxval(AT_HWCAP2);
123     arm_cpu_enable_crc32 = !!(features & HWCAP2_CRC32);
124     arm_cpu_enable_pmull = !!(features & HWCAP2_PMULL);
125 #elif defined(ARMV8_OS_FUCHSIA)
126     uint32_t features;
127     zx_status_t rc = zx_system_get_features(ZX_FEATURE_KIND_CPU, &features);
128     if (rc != ZX_OK || (features & ZX_ARM64_FEATURE_ISA_ASIMD) == 0)
129         return;  /* Report nothing if ASIMD(NEON) is missing */
130     arm_cpu_enable_crc32 = !!(features & ZX_ARM64_FEATURE_ISA_CRC32);
131     arm_cpu_enable_pmull = !!(features & ZX_ARM64_FEATURE_ISA_PMULL);
132 #elif defined(ARMV8_OS_WINDOWS)
133     arm_cpu_enable_crc32 = IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE);
134     arm_cpu_enable_pmull = IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE);
135 #elif defined(ARMV8_OS_FREEBSD)
136     uint64_t id_aa64isar0;
137     id_aa64isar0 = READ_SPECIALREG(id_aa64isar0_el1);
138     if (ID_AA64ISAR0_AES_VAL(id_aa64isar0) == ID_AA64ISAR0_AES_PMULL)
139         arm_cpu_enable_pmull = 1;
140     if (ID_AA64ISAR0_CRC32_VAL(id_aa64isar0) == ID_AA64ISAR0_CRC32_BASE)
141         arm_cpu_enable_crc32 = 1;
142 #endif
143 }
144 #endif
145 #elif defined(X86_NOT_WINDOWS) || defined(X86_WINDOWS)
146 /*
147  * iOS@x86 (i.e. emulator) is another special case where we disable
148  * SIMD optimizations.
149  */
150 #ifndef CPU_NO_SIMD
151 /* On x86 we simply use a instruction to check the CPU features.
152  * (i.e. CPUID).
153  */
_cpu_check_features(void)154 static void _cpu_check_features(void)
155 {
156     int x86_cpu_has_sse2;
157     int x86_cpu_has_ssse3;
158     int x86_cpu_has_sse42;
159     int x86_cpu_has_pclmulqdq;
160     int abcd[4];
161 
162 #ifdef _MSC_VER
163     __cpuid(abcd, 1);
164 #else
165     __cpuid(1, abcd[0], abcd[1], abcd[2], abcd[3]);
166 #endif
167 
168     x86_cpu_has_sse2 = abcd[3] & 0x4000000;
169     x86_cpu_has_ssse3 = abcd[2] & 0x000200;
170     x86_cpu_has_sse42 = abcd[2] & 0x100000;
171     x86_cpu_has_pclmulqdq = abcd[2] & 0x2;
172 
173     x86_cpu_enable_sse2 = x86_cpu_has_sse2;
174 
175     x86_cpu_enable_ssse3 = x86_cpu_has_ssse3;
176 
177     x86_cpu_enable_simd = x86_cpu_has_sse2 &&
178                           x86_cpu_has_sse42 &&
179                           x86_cpu_has_pclmulqdq;
180 }
181 #endif
182 #endif
183 #endif
184