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