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