1 /*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "instruction_set_features_x86.h"
18
19 #include <fstream>
20 #include <sstream>
21
22 #include <android-base/logging.h>
23 #include <android-base/stringprintf.h>
24 #include <android-base/strings.h>
25
26 #include "arch/x86_64/instruction_set_features_x86_64.h"
27 #include "base/array_ref.h"
28
29 #include <cpu_features_macros.h>
30
31 #ifdef CPU_FEATURES_ARCH_X86
32 // This header can only be included on x86 targets,
33 // as determined by cpu_features own define.
34 #include <cpuinfo_x86.h>
35 #endif
36
37 namespace art HIDDEN {
38
39 using android::base::StringPrintf;
40
41 // Feature-support arrays.
42
43 static constexpr const char* x86_known_variants[] = {
44 "atom",
45 "sandybridge",
46 "silvermont",
47 "goldmont",
48 "goldmont-plus",
49 "goldmont-without-sha-xsaves",
50 "tremont",
51 "kabylake",
52 "alderlake",
53 "default",
54 };
55
56 static constexpr const char* x86_variants_with_ssse3[] = {
57 "atom",
58 "sandybridge",
59 "silvermont",
60 "goldmont",
61 "goldmont-plus",
62 "goldmont-without-sha-xsaves",
63 "tremont",
64 "alderlake",
65 "kabylake",
66 };
67
68 static constexpr const char* x86_variants_with_sse4_1[] = {
69 "sandybridge",
70 "silvermont",
71 "goldmont",
72 "goldmont-plus",
73 "goldmont-without-sha-xsaves",
74 "tremont",
75 "alderlake",
76 "kabylake",
77 };
78
79 static constexpr const char* x86_variants_with_sse4_2[] = {
80 "sandybridge",
81 "silvermont",
82 "goldmont",
83 "goldmont-plus",
84 "goldmont-without-sha-xsaves",
85 "tremont",
86 "alderlake",
87 "kabylake",
88 };
89
90 static constexpr const char* x86_variants_with_popcnt[] = {
91 "sandybridge",
92 "silvermont",
93 "goldmont",
94 "goldmont-plus",
95 "goldmont-without-sha-xsaves",
96 "tremont",
97 "alderlake",
98 "kabylake",
99 };
100 static constexpr const char* x86_variants_with_avx[] = {
101 "kabylake",
102 "alderlake",
103 };
104
105 static constexpr const char* x86_variants_with_avx2[] = {
106 "kabylake",
107 "alderlake",
108 };
109
Create(bool x86_64,bool has_SSSE3,bool has_SSE4_1,bool has_SSE4_2,bool has_AVX,bool has_AVX2,bool has_POPCNT)110 X86FeaturesUniquePtr X86InstructionSetFeatures::Create(bool x86_64,
111 bool has_SSSE3,
112 bool has_SSE4_1,
113 bool has_SSE4_2,
114 bool has_AVX,
115 bool has_AVX2,
116 bool has_POPCNT) {
117 if (x86_64) {
118 return X86FeaturesUniquePtr(new X86_64InstructionSetFeatures(has_SSSE3,
119 has_SSE4_1,
120 has_SSE4_2,
121 has_AVX,
122 has_AVX2,
123 has_POPCNT));
124 } else {
125 return X86FeaturesUniquePtr(new X86InstructionSetFeatures(has_SSSE3,
126 has_SSE4_1,
127 has_SSE4_2,
128 has_AVX,
129 has_AVX2,
130 has_POPCNT));
131 }
132 }
133
FromVariant(const std::string & variant,std::string * error_msg,bool x86_64)134 X86FeaturesUniquePtr X86InstructionSetFeatures::FromVariant(const std::string& variant,
135 [[maybe_unused]] std::string* error_msg,
136 bool x86_64) {
137 const bool is_runtime_isa =
138 kRuntimeISA == (x86_64 ? InstructionSet::kX86_64 : InstructionSet::kX86);
139 if (is_runtime_isa && variant == "default") {
140 return FromCppDefines(x86_64);
141 }
142
143 bool has_SSSE3 = FindVariantInArray(x86_variants_with_ssse3, arraysize(x86_variants_with_ssse3),
144 variant);
145 bool has_SSE4_1 = FindVariantInArray(x86_variants_with_sse4_1,
146 arraysize(x86_variants_with_sse4_1),
147 variant);
148 bool has_SSE4_2 = FindVariantInArray(x86_variants_with_sse4_2,
149 arraysize(x86_variants_with_sse4_2),
150 variant);
151 bool has_AVX = FindVariantInArray(x86_variants_with_avx,
152 arraysize(x86_variants_with_avx),
153 variant);
154 bool has_AVX2 = FindVariantInArray(x86_variants_with_avx2,
155 arraysize(x86_variants_with_avx2),
156 variant);
157 bool has_POPCNT = FindVariantInArray(x86_variants_with_popcnt,
158 arraysize(x86_variants_with_popcnt),
159 variant);
160
161 // Verify that variant is known.
162 bool known_variant = FindVariantInArray(x86_known_variants, arraysize(x86_known_variants),
163 variant);
164 if (!known_variant) {
165 std::ostringstream os;
166 os << "Unexpected CPU variant for x86: " << variant << ".\n"
167 << "Known variants: "
168 << android::base::Join(ArrayRef<const char* const>(x86_known_variants), ", ");
169 LOG(WARNING) << os.str();
170 }
171
172 return Create(x86_64, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, has_AVX2, has_POPCNT);
173 }
174
FromBitmap(uint32_t bitmap,bool x86_64)175 X86FeaturesUniquePtr X86InstructionSetFeatures::FromBitmap(uint32_t bitmap, bool x86_64) {
176 bool has_SSSE3 = (bitmap & kSsse3Bitfield) != 0;
177 bool has_SSE4_1 = (bitmap & kSse4_1Bitfield) != 0;
178 bool has_SSE4_2 = (bitmap & kSse4_2Bitfield) != 0;
179 bool has_AVX = (bitmap & kAvxBitfield) != 0;
180 bool has_AVX2 = (bitmap & kAvxBitfield) != 0;
181 bool has_POPCNT = (bitmap & kPopCntBitfield) != 0;
182 return Create(x86_64, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, has_AVX2, has_POPCNT);
183 }
184
FromCppDefines(bool x86_64)185 X86FeaturesUniquePtr X86InstructionSetFeatures::FromCppDefines(bool x86_64) {
186 #ifndef __SSSE3__
187 const bool has_SSSE3 = false;
188 #else
189 const bool has_SSSE3 = true;
190 #endif
191
192 #ifndef __SSE4_1__
193 const bool has_SSE4_1 = false;
194 #else
195 const bool has_SSE4_1 = true;
196 #endif
197
198 #ifndef __SSE4_2__
199 const bool has_SSE4_2 = false;
200 #else
201 const bool has_SSE4_2 = true;
202 #endif
203
204 #ifndef __AVX__
205 const bool has_AVX = false;
206 #else
207 const bool has_AVX = true;
208 #endif
209
210 #ifndef __AVX2__
211 const bool has_AVX2 = false;
212 #else
213 const bool has_AVX2 = true;
214 #endif
215
216 #ifndef __POPCNT__
217 const bool has_POPCNT = false;
218 #else
219 const bool has_POPCNT = true;
220 #endif
221
222 return Create(x86_64, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, has_AVX2, has_POPCNT);
223 }
224
FromCpuInfo(bool x86_64)225 X86FeaturesUniquePtr X86InstructionSetFeatures::FromCpuInfo(bool x86_64) {
226 // Look in /proc/cpuinfo for features we need. Only use this when we can guarantee that
227 // the kernel puts the appropriate feature flags in here. Sometimes it doesn't.
228 bool has_SSSE3 = false;
229 bool has_SSE4_1 = false;
230 bool has_SSE4_2 = false;
231 bool has_AVX = false;
232 bool has_AVX2 = false;
233 bool has_POPCNT = false;
234
235 std::ifstream in("/proc/cpuinfo");
236 if (!in.fail()) {
237 while (!in.eof()) {
238 std::string line;
239 std::getline(in, line);
240 if (!in.eof()) {
241 LOG(INFO) << "cpuinfo line: " << line;
242 if (line.find("flags") != std::string::npos) {
243 LOG(INFO) << "found flags";
244 if (line.find("ssse3") != std::string::npos) {
245 has_SSSE3 = true;
246 }
247 if (line.find("sse4_1") != std::string::npos) {
248 has_SSE4_1 = true;
249 }
250 if (line.find("sse4_2") != std::string::npos) {
251 has_SSE4_2 = true;
252 }
253 if (line.find("avx") != std::string::npos) {
254 has_AVX = true;
255 }
256 if (line.find("avx2") != std::string::npos) {
257 has_AVX2 = true;
258 }
259 if (line.find("popcnt") != std::string::npos) {
260 has_POPCNT = true;
261 }
262 }
263 }
264 }
265 in.close();
266 } else {
267 LOG(ERROR) << "Failed to open /proc/cpuinfo";
268 }
269 return Create(x86_64, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, has_AVX2, has_POPCNT);
270 }
271
FromHwcap(bool x86_64)272 X86FeaturesUniquePtr X86InstructionSetFeatures::FromHwcap(bool x86_64) {
273 UNIMPLEMENTED(WARNING);
274 return FromCppDefines(x86_64);
275 }
276
FromAssembly(bool x86_64)277 X86FeaturesUniquePtr X86InstructionSetFeatures::FromAssembly(bool x86_64) {
278 UNIMPLEMENTED(WARNING);
279 return FromCppDefines(x86_64);
280 }
281
282
FromCpuFeatures(bool x86_64)283 X86FeaturesUniquePtr X86InstructionSetFeatures::FromCpuFeatures(bool x86_64) {
284 #ifdef CPU_FEATURES_ARCH_X86
285 cpu_features::X86Features features = cpu_features::GetX86Info().features;
286 return Create(x86_64,
287 features.ssse3,
288 features.sse4_1,
289 features.sse4_2,
290 features.avx,
291 features.avx2,
292 features.popcnt);
293 #else
294 UNIMPLEMENTED(WARNING);
295 return FromCppDefines(x86_64);
296 #endif
297 }
298
299
Equals(const InstructionSetFeatures * other) const300 bool X86InstructionSetFeatures::Equals(const InstructionSetFeatures* other) const {
301 if (GetInstructionSet() != other->GetInstructionSet()) {
302 return false;
303 }
304 const X86InstructionSetFeatures* other_as_x86 = other->AsX86InstructionSetFeatures();
305 return (has_SSSE3_ == other_as_x86->has_SSSE3_) &&
306 (has_SSE4_1_ == other_as_x86->has_SSE4_1_) &&
307 (has_SSE4_2_ == other_as_x86->has_SSE4_2_) &&
308 (has_AVX_ == other_as_x86->has_AVX_) &&
309 (has_AVX2_ == other_as_x86->has_AVX2_) &&
310 (has_POPCNT_ == other_as_x86->has_POPCNT_);
311 }
312
HasAtLeast(const InstructionSetFeatures * other) const313 bool X86InstructionSetFeatures::HasAtLeast(const InstructionSetFeatures* other) const {
314 if (GetInstructionSet() != other->GetInstructionSet()) {
315 return false;
316 }
317 const X86InstructionSetFeatures* other_as_x86 = other->AsX86InstructionSetFeatures();
318 return (has_SSSE3_ || !other_as_x86->has_SSSE3_) &&
319 (has_SSE4_1_ || !other_as_x86->has_SSE4_1_) &&
320 (has_SSE4_2_ || !other_as_x86->has_SSE4_2_) &&
321 (has_AVX_ || !other_as_x86->has_AVX_) &&
322 (has_AVX2_ || !other_as_x86->has_AVX2_) &&
323 (has_POPCNT_ || !other_as_x86->has_POPCNT_);
324 }
325
AsBitmap() const326 uint32_t X86InstructionSetFeatures::AsBitmap() const {
327 return (has_SSSE3_ ? kSsse3Bitfield : 0) |
328 (has_SSE4_1_ ? kSse4_1Bitfield : 0) |
329 (has_SSE4_2_ ? kSse4_2Bitfield : 0) |
330 (has_AVX_ ? kAvxBitfield : 0) |
331 (has_AVX2_ ? kAvx2Bitfield : 0) |
332 (has_POPCNT_ ? kPopCntBitfield : 0);
333 }
334
GetFeatureString() const335 std::string X86InstructionSetFeatures::GetFeatureString() const {
336 std::string result;
337 if (has_SSSE3_) {
338 result += "ssse3";
339 } else {
340 result += "-ssse3";
341 }
342 if (has_SSE4_1_) {
343 result += ",sse4.1";
344 } else {
345 result += ",-sse4.1";
346 }
347 if (has_SSE4_2_) {
348 result += ",sse4.2";
349 } else {
350 result += ",-sse4.2";
351 }
352 if (has_AVX_) {
353 result += ",avx";
354 } else {
355 result += ",-avx";
356 }
357 if (has_AVX2_) {
358 result += ",avx2";
359 } else {
360 result += ",-avx2";
361 }
362 if (has_POPCNT_) {
363 result += ",popcnt";
364 } else {
365 result += ",-popcnt";
366 }
367 return result;
368 }
369
AddFeaturesFromSplitString(const std::vector<std::string> & features,bool x86_64,std::string * error_msg) const370 std::unique_ptr<const InstructionSetFeatures> X86InstructionSetFeatures::AddFeaturesFromSplitString(
371 const std::vector<std::string>& features, bool x86_64,
372 std::string* error_msg) const {
373 bool has_SSSE3 = has_SSSE3_;
374 bool has_SSE4_1 = has_SSE4_1_;
375 bool has_SSE4_2 = has_SSE4_2_;
376 bool has_AVX = has_AVX_;
377 bool has_AVX2 = has_AVX2_;
378 bool has_POPCNT = has_POPCNT_;
379 for (const std::string& feature : features) {
380 DCHECK_EQ(android::base::Trim(feature), feature)
381 << "Feature name is not trimmed: '" << feature << "'";
382 if (feature == "ssse3") {
383 has_SSSE3 = true;
384 } else if (feature == "-ssse3") {
385 has_SSSE3 = false;
386 } else if (feature == "sse4.1") {
387 has_SSE4_1 = true;
388 } else if (feature == "-sse4.1") {
389 has_SSE4_1 = false;
390 } else if (feature == "sse4.2") {
391 has_SSE4_2 = true;
392 } else if (feature == "-sse4.2") {
393 has_SSE4_2 = false;
394 } else if (feature == "avx") {
395 has_AVX = true;
396 } else if (feature == "-avx") {
397 has_AVX = false;
398 } else if (feature == "avx2") {
399 has_AVX2 = true;
400 } else if (feature == "-avx2") {
401 has_AVX2 = false;
402 } else if (feature == "popcnt") {
403 has_POPCNT = true;
404 } else if (feature == "-popcnt") {
405 has_POPCNT = false;
406 } else {
407 *error_msg = StringPrintf("Unknown instruction set feature: '%s'", feature.c_str());
408 return nullptr;
409 }
410 }
411 return Create(x86_64, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, has_AVX2, has_POPCNT);
412 }
413
414 } // namespace art
415