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) |
35 | #include <pthread.h> |
36 | #endif |
37 | |
38 | #if defined(ARMV8_OS_ANDROID) |
39 | #include <cpu-features.h> |
40 | #elif defined(ARMV8_OS_LINUX) |
41 | #include <asm/hwcap.h> |
42 | #include <sys/auxv.h> |
43 | #elif defined(ARMV8_OS_FUCHSIA) |
44 | #include <zircon/features.h> |
45 | #include <zircon/syscalls.h> |
46 | #include <zircon/types.h> |
47 | #elif defined(ARMV8_OS_WINDOWS) || defined(X86_WINDOWS) |
48 | #include <windows.h> |
49 | #elif !defined(_MSC_VER) |
50 | #include <pthread.h> |
51 | #else |
52 | #error cpu_features.c CPU feature detection in not defined for your platform |
53 | #endif |
54 | |
55 | #if !defined(CPU_NO_SIMD) && !defined(ARMV8_OS_MACOS) && !defined(ARM_OS_IOS) |
56 | static void _cpu_check_features(void); |
57 | #endif |
58 | |
59 | #if defined(ARMV8_OS_ANDROID) || defined(ARMV8_OS_LINUX) || defined(ARMV8_OS_MACOS) || defined(ARMV8_OS_FUCHSIA) || defined(X86_NOT_WINDOWS) |
60 | #if !defined(ARMV8_OS_MACOS) |
61 | // _cpu_check_features() doesn't need to do anything on mac/arm since all |
62 | // features are known at build time, so don't call it. |
63 | // Do provide cpu_check_features() (with a no-op implementation) so that we |
64 | // don't have to make all callers of it check for mac/arm. |
65 | static pthread_once_t cpu_check_inited_once = PTHREAD_ONCE_INIT; |
66 | #endif |
67 | void ZLIB_INTERNAL cpu_check_features(void) |
68 | { |
69 | #if !defined(ARMV8_OS_MACOS) |
70 | pthread_once(&cpu_check_inited_once, _cpu_check_features); |
71 | #endif |
72 | } |
73 | #elif defined(ARMV8_OS_WINDOWS) || defined(X86_WINDOWS) |
74 | static INIT_ONCE cpu_check_inited_once = INIT_ONCE_STATIC_INIT; |
75 | static BOOL CALLBACK _cpu_check_features_forwarder(PINIT_ONCE once, PVOID param, PVOID* context) |
76 | { |
77 | _cpu_check_features(); |
78 | return TRUE; |
79 | } |
80 | void ZLIB_INTERNAL cpu_check_features(void) |
81 | { |
82 | InitOnceExecuteOnce(&cpu_check_inited_once, _cpu_check_features_forwarder, |
83 | NULL, NULL); |
84 | } |
85 | #endif |
86 | |
87 | #if (defined(__ARM_NEON__) || defined(__ARM_NEON)) |
88 | /* |
89 | * iOS@ARM is a special case where we always have NEON but don't check |
90 | * for crypto extensions. |
91 | */ |
92 | #if !defined(ARMV8_OS_MACOS) && !defined(ARM_OS_IOS) |
93 | /* |
94 | * See http://bit.ly/2CcoEsr for run-time detection of ARM features and also |
95 | * crbug.com/931275 for android_getCpuFeatures() use in the Android sandbox. |
96 | */ |
97 | static void _cpu_check_features(void) |
98 | { |
99 | #if defined(ARMV8_OS_ANDROID) && defined(__aarch64__) |
100 | uint64_t features = android_getCpuFeatures(); |
101 | arm_cpu_enable_crc32 = !!(features & ANDROID_CPU_ARM64_FEATURE_CRC32); |
102 | arm_cpu_enable_pmull = !!(features & ANDROID_CPU_ARM64_FEATURE_PMULL); |
103 | #elif defined(ARMV8_OS_ANDROID) /* aarch32 */ |
104 | uint64_t features = android_getCpuFeatures(); |
105 | arm_cpu_enable_crc32 = !!(features & ANDROID_CPU_ARM_FEATURE_CRC32); |
106 | arm_cpu_enable_pmull = !!(features & ANDROID_CPU_ARM_FEATURE_PMULL); |
107 | #elif defined(ARMV8_OS_LINUX) && defined(__aarch64__) |
108 | unsigned long features = getauxval(AT_HWCAP); |
109 | arm_cpu_enable_crc32 = !!(features & HWCAP_CRC32); |
110 | arm_cpu_enable_pmull = !!(features & HWCAP_PMULL); |
111 | #elif defined(ARMV8_OS_LINUX) && (defined(__ARM_NEON) || defined(__ARM_NEON__)) |
112 | /* Query HWCAP2 for ARMV8-A SoCs running in aarch32 mode */ |
113 | unsigned long features = getauxval(AT_HWCAP2); |
114 | arm_cpu_enable_crc32 = !!(features & HWCAP2_CRC32); |
115 | arm_cpu_enable_pmull = !!(features & HWCAP2_PMULL); |
116 | #elif defined(ARMV8_OS_FUCHSIA) |
117 | uint32_t features; |
118 | zx_status_t rc = zx_system_get_features(ZX_FEATURE_KIND_CPU, &features); |
119 | if (rc != ZX_OK || (features & ZX_ARM64_FEATURE_ISA_ASIMD) == 0) |
120 | return; /* Report nothing if ASIMD(NEON) is missing */ |
121 | arm_cpu_enable_crc32 = !!(features & ZX_ARM64_FEATURE_ISA_CRC32); |
122 | arm_cpu_enable_pmull = !!(features & ZX_ARM64_FEATURE_ISA_PMULL); |
123 | #elif defined(ARMV8_OS_WINDOWS) |
124 | arm_cpu_enable_crc32 = IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE); |
125 | arm_cpu_enable_pmull = IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE); |
126 | #endif |
127 | } |
128 | #endif |
129 | #elif defined(X86_NOT_WINDOWS) || defined(X86_WINDOWS) |
130 | /* |
131 | * iOS@x86 (i.e. emulator) is another special case where we disable |
132 | * SIMD optimizations. |
133 | */ |
134 | #ifndef CPU_NO_SIMD |
135 | /* On x86 we simply use a instruction to check the CPU features. |
136 | * (i.e. CPUID). |
137 | */ |
138 | static void _cpu_check_features(void) |
139 | { |
140 | int x86_cpu_has_sse2; |
141 | int x86_cpu_has_ssse3; |
142 | int x86_cpu_has_sse42; |
143 | int x86_cpu_has_pclmulqdq; |
144 | int abcd[4]; |
145 | |
146 | #ifdef _MSC_VER |
147 | __cpuid(abcd, 1); |
148 | #else |
149 | __cpuid(1, abcd[0], abcd[1], abcd[2], abcd[3]); |
150 | #endif |
151 | |
152 | x86_cpu_has_sse2 = abcd[3] & 0x4000000; |
153 | x86_cpu_has_ssse3 = abcd[2] & 0x000200; |
154 | x86_cpu_has_sse42 = abcd[2] & 0x100000; |
155 | x86_cpu_has_pclmulqdq = abcd[2] & 0x2; |
156 | |
157 | x86_cpu_enable_sse2 = x86_cpu_has_sse2; |
158 | |
159 | x86_cpu_enable_ssse3 = x86_cpu_has_ssse3; |
160 | |
161 | x86_cpu_enable_simd = x86_cpu_has_sse2 && |
162 | x86_cpu_has_sse42 && |
163 | x86_cpu_has_pclmulqdq; |
164 | } |
165 | #endif |
166 | #endif |
167 | #endif |
168 | |