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