1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
/*
* MSVC wants us to use the non-portable _dupenv_s instead; since we need
* to remain portable, tell MSVC to suppress this warning.
*/
#define _CRT_SECURE_NO_WARNINGS
#include <aws/common/cpuid.h>
#include <stdlib.h>
extern void aws_run_cpuid(uint32_t eax, uint32_t ecx, uint32_t *abcd);
typedef bool(has_feature_fn)(void);
static bool s_has_clmul(void) {
uint32_t abcd[4];
uint32_t clmul_mask = 0x00000002;
aws_run_cpuid(1, 0, abcd);
if ((abcd[2] & clmul_mask) != clmul_mask)
return false;
return true;
}
static bool s_has_sse41(void) {
uint32_t abcd[4];
uint32_t sse41_mask = 0x00080000;
aws_run_cpuid(1, 0, abcd);
if ((abcd[2] & sse41_mask) != sse41_mask)
return false;
return true;
}
static bool s_has_sse42(void) {
uint32_t abcd[4];
uint32_t sse42_mask = 0x00100000;
aws_run_cpuid(1, 0, abcd);
if ((abcd[2] & sse42_mask) != sse42_mask)
return false;
return true;
}
static bool s_has_avx2(void) {
uint32_t abcd[4];
/* Check AVX2:
* CPUID.(EAX=07H, ECX=0H):EBX.AVX2[bit 5]==1 */
uint32_t avx2_mask = (1 << 5);
aws_run_cpuid(7, 0, abcd);
if ((abcd[1] & avx2_mask) != avx2_mask) {
return false;
}
/* Also check AVX:
* CPUID.(EAX=01H, ECX=0H):ECX.AVX[bit 28]==1
*
* NOTE: It SHOULD be impossible for a CPU to support AVX2 without supporting AVX.
* But we've received crash reports where the AVX2 feature check passed
* and then an AVX instruction caused an "invalid instruction" crash.
*
* We diagnosed these machines by asking users to run the sample program from:
* https://docs.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex?view=msvc-160
* and observed the following results:
*
* AVX not supported
* AVX2 supported
*
* We don't know for sure what was up with those machines, but this extra
* check should stop them from running our AVX/AVX2 code paths. */
uint32_t avx1_mask = (1 << 28);
aws_run_cpuid(1, 0, abcd);
if ((abcd[2] & avx1_mask) != avx1_mask) {
return false;
}
return true;
}
static bool s_has_bmi2(void) {
uint32_t abcd[4];
/* Check BMI2:
* CPUID.(EAX=07H, ECX=0H):EBX.BMI2[bit 8]==1 */
uint32_t bmi2_mask = (1 << 8);
aws_run_cpuid(7, 0, abcd);
if ((abcd[1] & bmi2_mask) != bmi2_mask) {
return false;
}
return true;
}
has_feature_fn *s_check_cpu_feature[AWS_CPU_FEATURE_COUNT] = {
[AWS_CPU_FEATURE_CLMUL] = s_has_clmul,
[AWS_CPU_FEATURE_SSE_4_1] = s_has_sse41,
[AWS_CPU_FEATURE_SSE_4_2] = s_has_sse42,
[AWS_CPU_FEATURE_AVX2] = s_has_avx2,
[AWS_CPU_FEATURE_BMI2] = s_has_bmi2,
};
bool aws_cpu_has_feature(enum aws_cpu_feature_name feature_name) {
if (s_check_cpu_feature[feature_name])
return s_check_cpu_feature[feature_name]();
return false;
}
#define CPUID_AVAILABLE 0
#define CPUID_UNAVAILABLE 1
static int cpuid_state = 2;
bool aws_common_private_has_avx2(void) {
if (AWS_LIKELY(cpuid_state == 0)) {
return true;
}
if (AWS_LIKELY(cpuid_state == 1)) {
return false;
}
/* Provide a hook for testing fallbacks and benchmarking */
const char *env_avx2_enabled = getenv("AWS_COMMON_AVX2");
if (env_avx2_enabled) {
int is_enabled = atoi(env_avx2_enabled);
cpuid_state = !is_enabled;
return is_enabled;
}
bool available = aws_cpu_has_feature(AWS_CPU_FEATURE_AVX2);
cpuid_state = available ? CPUID_AVAILABLE : CPUID_UNAVAILABLE;
return available;
}
|