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 "default",
53 };
54
55 static constexpr const char* x86_variants_with_ssse3[] = {
56 "atom",
57 "sandybridge",
58 "silvermont",
59 "goldmont",
60 "goldmont-plus",
61 "goldmont-without-sha-xsaves",
62 "tremont",
63 "kabylake",
64 };
65
66 static constexpr const char* x86_variants_with_sse4_1[] = {
67 "sandybridge",
68 "silvermont",
69 "goldmont",
70 "goldmont-plus",
71 "goldmont-without-sha-xsaves",
72 "tremont",
73 "kabylake",
74 };
75
76 static constexpr const char* x86_variants_with_sse4_2[] = {
77 "sandybridge",
78 "silvermont",
79 "goldmont",
80 "goldmont-plus",
81 "goldmont-without-sha-xsaves",
82 "tremont",
83 "kabylake",
84 };
85
86 static constexpr const char* x86_variants_with_popcnt[] = {
87 "sandybridge",
88 "silvermont",
89 "goldmont",
90 "goldmont-plus",
91 "goldmont-without-sha-xsaves",
92 "tremont",
93 "kabylake",
94 };
95 static constexpr const char* x86_variants_with_avx[] = {
96 "kabylake",
97 };
98
99 static constexpr const char* x86_variants_with_avx2[] = {
100 "kabylake",
101 };
102
Create(bool x86_64,bool has_SSSE3,bool has_SSE4_1,bool has_SSE4_2,bool has_AVX,bool has_AVX2,bool has_POPCNT)103 X86FeaturesUniquePtr X86InstructionSetFeatures::Create(bool x86_64,
104 bool has_SSSE3,
105 bool has_SSE4_1,
106 bool has_SSE4_2,
107 bool has_AVX,
108 bool has_AVX2,
109 bool has_POPCNT) {
110 if (x86_64) {
111 return X86FeaturesUniquePtr(new X86_64InstructionSetFeatures(has_SSSE3,
112 has_SSE4_1,
113 has_SSE4_2,
114 has_AVX,
115 has_AVX2,
116 has_POPCNT));
117 } else {
118 return X86FeaturesUniquePtr(new X86InstructionSetFeatures(has_SSSE3,
119 has_SSE4_1,
120 has_SSE4_2,
121 has_AVX,
122 has_AVX2,
123 has_POPCNT));
124 }
125 }
126
FromVariant(const std::string & variant,std::string * error_msg,bool x86_64)127 X86FeaturesUniquePtr X86InstructionSetFeatures::FromVariant(const std::string& variant,
128 [[maybe_unused]] std::string* error_msg,
129 bool x86_64) {
130 const bool is_runtime_isa =
131 kRuntimeISA == (x86_64 ? InstructionSet::kX86_64 : InstructionSet::kX86);
132 if (is_runtime_isa && variant == "default") {
133 return FromCppDefines(x86_64);
134 }
135
136 bool has_SSSE3 = FindVariantInArray(x86_variants_with_ssse3, arraysize(x86_variants_with_ssse3),
137 variant);
138 bool has_SSE4_1 = FindVariantInArray(x86_variants_with_sse4_1,
139 arraysize(x86_variants_with_sse4_1),
140 variant);
141 bool has_SSE4_2 = FindVariantInArray(x86_variants_with_sse4_2,
142 arraysize(x86_variants_with_sse4_2),
143 variant);
144 bool has_AVX = FindVariantInArray(x86_variants_with_avx,
145 arraysize(x86_variants_with_avx),
146 variant);
147 bool has_AVX2 = FindVariantInArray(x86_variants_with_avx2,
148 arraysize(x86_variants_with_avx2),
149 variant);
150 bool has_POPCNT = FindVariantInArray(x86_variants_with_popcnt,
151 arraysize(x86_variants_with_popcnt),
152 variant);
153
154 // Verify that variant is known.
155 bool known_variant = FindVariantInArray(x86_known_variants, arraysize(x86_known_variants),
156 variant);
157 if (!known_variant) {
158 std::ostringstream os;
159 os << "Unexpected CPU variant for x86: " << variant << ".\n"
160 << "Known variants: "
161 << android::base::Join(ArrayRef<const char* const>(x86_known_variants), ", ");
162 LOG(WARNING) << os.str();
163 }
164
165 return Create(x86_64, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, has_AVX2, has_POPCNT);
166 }
167
FromBitmap(uint32_t bitmap,bool x86_64)168 X86FeaturesUniquePtr X86InstructionSetFeatures::FromBitmap(uint32_t bitmap, bool x86_64) {
169 bool has_SSSE3 = (bitmap & kSsse3Bitfield) != 0;
170 bool has_SSE4_1 = (bitmap & kSse4_1Bitfield) != 0;
171 bool has_SSE4_2 = (bitmap & kSse4_2Bitfield) != 0;
172 bool has_AVX = (bitmap & kAvxBitfield) != 0;
173 bool has_AVX2 = (bitmap & kAvxBitfield) != 0;
174 bool has_POPCNT = (bitmap & kPopCntBitfield) != 0;
175 return Create(x86_64, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, has_AVX2, has_POPCNT);
176 }
177
FromCppDefines(bool x86_64)178 X86FeaturesUniquePtr X86InstructionSetFeatures::FromCppDefines(bool x86_64) {
179 #ifndef __SSSE3__
180 const bool has_SSSE3 = false;
181 #else
182 const bool has_SSSE3 = true;
183 #endif
184
185 #ifndef __SSE4_1__
186 const bool has_SSE4_1 = false;
187 #else
188 const bool has_SSE4_1 = true;
189 #endif
190
191 #ifndef __SSE4_2__
192 const bool has_SSE4_2 = false;
193 #else
194 const bool has_SSE4_2 = true;
195 #endif
196
197 #ifndef __AVX__
198 const bool has_AVX = false;
199 #else
200 const bool has_AVX = true;
201 #endif
202
203 #ifndef __AVX2__
204 const bool has_AVX2 = false;
205 #else
206 const bool has_AVX2 = true;
207 #endif
208
209 #ifndef __POPCNT__
210 const bool has_POPCNT = false;
211 #else
212 const bool has_POPCNT = true;
213 #endif
214
215 return Create(x86_64, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, has_AVX2, has_POPCNT);
216 }
217
FromCpuInfo(bool x86_64)218 X86FeaturesUniquePtr X86InstructionSetFeatures::FromCpuInfo(bool x86_64) {
219 // Look in /proc/cpuinfo for features we need. Only use this when we can guarantee that
220 // the kernel puts the appropriate feature flags in here. Sometimes it doesn't.
221 bool has_SSSE3 = false;
222 bool has_SSE4_1 = false;
223 bool has_SSE4_2 = false;
224 bool has_AVX = false;
225 bool has_AVX2 = false;
226 bool has_POPCNT = false;
227
228 std::ifstream in("/proc/cpuinfo");
229 if (!in.fail()) {
230 while (!in.eof()) {
231 std::string line;
232 std::getline(in, line);
233 if (!in.eof()) {
234 LOG(INFO) << "cpuinfo line: " << line;
235 if (line.find("flags") != std::string::npos) {
236 LOG(INFO) << "found flags";
237 if (line.find("ssse3") != std::string::npos) {
238 has_SSSE3 = true;
239 }
240 if (line.find("sse4_1") != std::string::npos) {
241 has_SSE4_1 = true;
242 }
243 if (line.find("sse4_2") != std::string::npos) {
244 has_SSE4_2 = true;
245 }
246 if (line.find("avx") != std::string::npos) {
247 has_AVX = true;
248 }
249 if (line.find("avx2") != std::string::npos) {
250 has_AVX2 = true;
251 }
252 if (line.find("popcnt") != std::string::npos) {
253 has_POPCNT = true;
254 }
255 }
256 }
257 }
258 in.close();
259 } else {
260 LOG(ERROR) << "Failed to open /proc/cpuinfo";
261 }
262 return Create(x86_64, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, has_AVX2, has_POPCNT);
263 }
264
FromHwcap(bool x86_64)265 X86FeaturesUniquePtr X86InstructionSetFeatures::FromHwcap(bool x86_64) {
266 UNIMPLEMENTED(WARNING);
267 return FromCppDefines(x86_64);
268 }
269
FromAssembly(bool x86_64)270 X86FeaturesUniquePtr X86InstructionSetFeatures::FromAssembly(bool x86_64) {
271 UNIMPLEMENTED(WARNING);
272 return FromCppDefines(x86_64);
273 }
274
275
FromCpuFeatures(bool x86_64)276 X86FeaturesUniquePtr X86InstructionSetFeatures::FromCpuFeatures(bool x86_64) {
277 #ifdef CPU_FEATURES_ARCH_X86
278 cpu_features::X86Features features = cpu_features::GetX86Info().features;
279 return Create(x86_64,
280 features.ssse3,
281 features.sse4_1,
282 features.sse4_2,
283 features.avx,
284 features.avx2,
285 features.popcnt);
286 #else
287 UNIMPLEMENTED(WARNING);
288 return FromCppDefines(x86_64);
289 #endif
290 }
291
292
Equals(const InstructionSetFeatures * other) const293 bool X86InstructionSetFeatures::Equals(const InstructionSetFeatures* other) const {
294 if (GetInstructionSet() != other->GetInstructionSet()) {
295 return false;
296 }
297 const X86InstructionSetFeatures* other_as_x86 = other->AsX86InstructionSetFeatures();
298 return (has_SSSE3_ == other_as_x86->has_SSSE3_) &&
299 (has_SSE4_1_ == other_as_x86->has_SSE4_1_) &&
300 (has_SSE4_2_ == other_as_x86->has_SSE4_2_) &&
301 (has_AVX_ == other_as_x86->has_AVX_) &&
302 (has_AVX2_ == other_as_x86->has_AVX2_) &&
303 (has_POPCNT_ == other_as_x86->has_POPCNT_);
304 }
305
HasAtLeast(const InstructionSetFeatures * other) const306 bool X86InstructionSetFeatures::HasAtLeast(const InstructionSetFeatures* other) const {
307 if (GetInstructionSet() != other->GetInstructionSet()) {
308 return false;
309 }
310 const X86InstructionSetFeatures* other_as_x86 = other->AsX86InstructionSetFeatures();
311 return (has_SSSE3_ || !other_as_x86->has_SSSE3_) &&
312 (has_SSE4_1_ || !other_as_x86->has_SSE4_1_) &&
313 (has_SSE4_2_ || !other_as_x86->has_SSE4_2_) &&
314 (has_AVX_ || !other_as_x86->has_AVX_) &&
315 (has_AVX2_ || !other_as_x86->has_AVX2_) &&
316 (has_POPCNT_ || !other_as_x86->has_POPCNT_);
317 }
318
AsBitmap() const319 uint32_t X86InstructionSetFeatures::AsBitmap() const {
320 return (has_SSSE3_ ? kSsse3Bitfield : 0) |
321 (has_SSE4_1_ ? kSse4_1Bitfield : 0) |
322 (has_SSE4_2_ ? kSse4_2Bitfield : 0) |
323 (has_AVX_ ? kAvxBitfield : 0) |
324 (has_AVX2_ ? kAvx2Bitfield : 0) |
325 (has_POPCNT_ ? kPopCntBitfield : 0);
326 }
327
GetFeatureString() const328 std::string X86InstructionSetFeatures::GetFeatureString() const {
329 std::string result;
330 if (has_SSSE3_) {
331 result += "ssse3";
332 } else {
333 result += "-ssse3";
334 }
335 if (has_SSE4_1_) {
336 result += ",sse4.1";
337 } else {
338 result += ",-sse4.1";
339 }
340 if (has_SSE4_2_) {
341 result += ",sse4.2";
342 } else {
343 result += ",-sse4.2";
344 }
345 if (has_AVX_) {
346 result += ",avx";
347 } else {
348 result += ",-avx";
349 }
350 if (has_AVX2_) {
351 result += ",avx2";
352 } else {
353 result += ",-avx2";
354 }
355 if (has_POPCNT_) {
356 result += ",popcnt";
357 } else {
358 result += ",-popcnt";
359 }
360 return result;
361 }
362
AddFeaturesFromSplitString(const std::vector<std::string> & features,bool x86_64,std::string * error_msg) const363 std::unique_ptr<const InstructionSetFeatures> X86InstructionSetFeatures::AddFeaturesFromSplitString(
364 const std::vector<std::string>& features, bool x86_64,
365 std::string* error_msg) const {
366 bool has_SSSE3 = has_SSSE3_;
367 bool has_SSE4_1 = has_SSE4_1_;
368 bool has_SSE4_2 = has_SSE4_2_;
369 bool has_AVX = has_AVX_;
370 bool has_AVX2 = has_AVX2_;
371 bool has_POPCNT = has_POPCNT_;
372 for (const std::string& feature : features) {
373 DCHECK_EQ(android::base::Trim(feature), feature)
374 << "Feature name is not trimmed: '" << feature << "'";
375 if (feature == "ssse3") {
376 has_SSSE3 = true;
377 } else if (feature == "-ssse3") {
378 has_SSSE3 = false;
379 } else if (feature == "sse4.1") {
380 has_SSE4_1 = true;
381 } else if (feature == "-sse4.1") {
382 has_SSE4_1 = false;
383 } else if (feature == "sse4.2") {
384 has_SSE4_2 = true;
385 } else if (feature == "-sse4.2") {
386 has_SSE4_2 = false;
387 } else if (feature == "avx") {
388 has_AVX = true;
389 } else if (feature == "-avx") {
390 has_AVX = false;
391 } else if (feature == "avx2") {
392 has_AVX2 = true;
393 } else if (feature == "-avx2") {
394 has_AVX2 = false;
395 } else if (feature == "popcnt") {
396 has_POPCNT = true;
397 } else if (feature == "-popcnt") {
398 has_POPCNT = false;
399 } else {
400 *error_msg = StringPrintf("Unknown instruction set feature: '%s'", feature.c_str());
401 return nullptr;
402 }
403 }
404 return Create(x86_64, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, has_AVX2, has_POPCNT);
405 }
406
407 } // namespace art
408