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. */
23int ZLIB_INTERNAL arm_cpu_enable_crc32 = 1;
24#else
25int ZLIB_INTERNAL arm_cpu_enable_crc32 = 0;
26#endif
27int ZLIB_INTERNAL arm_cpu_enable_pmull = 0;
28int ZLIB_INTERNAL x86_cpu_enable_sse2 = 0;
29int ZLIB_INTERNAL x86_cpu_enable_ssse3 = 0;
30int 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)
56static 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.
65static pthread_once_t cpu_check_inited_once = PTHREAD_ONCE_INIT;
66#endif
67void 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)
74static INIT_ONCE cpu_check_inited_once = INIT_ONCE_STATIC_INIT;
75static BOOL CALLBACK _cpu_check_features_forwarder(PINIT_ONCE once, PVOID param, PVOID* context)
76{
77 _cpu_check_features();
78 return TRUE;
79}
80void 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 */
97static 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 */
138static 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