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 "arch/x86_64/instruction_set_features_x86_64.h"
23 #include "base/stringprintf.h"
24 #include "utils.h"  // For Trim.
25 
26 namespace art {
27 
28 // Feature-support arrays.
29 
30 static constexpr const char* x86_known_variants[] = {
31     "atom",
32     "silvermont",
33 };
34 
35 static constexpr const char* x86_variants_with_ssse3[] = {
36     "atom",
37     "silvermont",
38 };
39 
40 static constexpr const char* x86_variants_with_sse4_1[] = {
41     "silvermont",
42 };
43 
44 static constexpr const char* x86_variants_with_sse4_2[] = {
45     "silvermont",
46 };
47 
FromVariant(const std::string & variant,std::string * error_msg ATTRIBUTE_UNUSED,bool x86_64)48 const X86InstructionSetFeatures* X86InstructionSetFeatures::FromVariant(
49     const std::string& variant, std::string* error_msg ATTRIBUTE_UNUSED,
50     bool x86_64) {
51   bool smp = true;  // Conservative default.
52   bool has_SSSE3 = FindVariantInArray(x86_variants_with_ssse3, arraysize(x86_variants_with_ssse3),
53                                       variant);
54   bool has_SSE4_1 = FindVariantInArray(x86_variants_with_sse4_1,
55                                        arraysize(x86_variants_with_sse4_1),
56                                        variant);
57   bool has_SSE4_2 = FindVariantInArray(x86_variants_with_sse4_2,
58                                        arraysize(x86_variants_with_sse4_2),
59                                        variant);
60   bool has_AVX = false;
61   bool has_AVX2 = false;
62 
63   bool known_variant = FindVariantInArray(x86_known_variants, arraysize(x86_known_variants),
64                                           variant);
65   if (!known_variant && variant != "default") {
66     LOG(WARNING) << "Unexpected CPU variant for X86 using defaults: " << variant;
67   }
68 
69   if (x86_64) {
70     return new X86_64InstructionSetFeatures(smp, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX,
71                                             has_AVX2);
72   } else {
73     return new X86InstructionSetFeatures(smp, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX,
74                                             has_AVX2);
75   }
76 }
77 
FromBitmap(uint32_t bitmap,bool x86_64)78 const X86InstructionSetFeatures* X86InstructionSetFeatures::FromBitmap(uint32_t bitmap,
79                                                                        bool x86_64) {
80   bool smp = (bitmap & kSmpBitfield) != 0;
81   bool has_SSSE3 = (bitmap & kSsse3Bitfield) != 0;
82   bool has_SSE4_1 = (bitmap & kSse4_1Bitfield) != 0;
83   bool has_SSE4_2 = (bitmap & kSse4_2Bitfield) != 0;
84   bool has_AVX = (bitmap & kAvxBitfield) != 0;
85   bool has_AVX2 = (bitmap & kAvxBitfield) != 0;
86   if (x86_64) {
87     return new X86_64InstructionSetFeatures(smp, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, has_AVX2);
88   } else {
89     return new X86InstructionSetFeatures(smp, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX,
90                                             has_AVX2);
91   }
92 }
93 
FromCppDefines(bool x86_64)94 const X86InstructionSetFeatures* X86InstructionSetFeatures::FromCppDefines(bool x86_64) {
95   const bool smp = true;
96 
97 #ifndef __SSSE3__
98   const bool has_SSSE3 = false;
99 #else
100   const bool has_SSSE3 = true;
101 #endif
102 
103 #ifndef __SSE4_1__
104   const bool has_SSE4_1 = false;
105 #else
106   const bool has_SSE4_1 = true;
107 #endif
108 
109 #ifndef __SSE4_2__
110   const bool has_SSE4_2 = false;
111 #else
112   const bool has_SSE4_2 = true;
113 #endif
114 
115 #ifndef __AVX__
116   const bool has_AVX = false;
117 #else
118   const bool has_AVX = true;
119 #endif
120 
121 #ifndef __AVX2__
122   const bool has_AVX2 = false;
123 #else
124   const bool has_AVX2 = true;
125 #endif
126 
127   if (x86_64) {
128     return new X86_64InstructionSetFeatures(smp, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, has_AVX2);
129   } else {
130     return new X86InstructionSetFeatures(smp, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX,
131                                             has_AVX2);
132   }
133 }
134 
FromCpuInfo(bool x86_64)135 const X86InstructionSetFeatures* X86InstructionSetFeatures::FromCpuInfo(bool x86_64) {
136   // Look in /proc/cpuinfo for features we need.  Only use this when we can guarantee that
137   // the kernel puts the appropriate feature flags in here.  Sometimes it doesn't.
138   bool smp = false;
139   bool has_SSSE3 = false;
140   bool has_SSE4_1 = false;
141   bool has_SSE4_2 = false;
142   bool has_AVX = false;
143   bool has_AVX2 = false;
144 
145   std::ifstream in("/proc/cpuinfo");
146   if (!in.fail()) {
147     while (!in.eof()) {
148       std::string line;
149       std::getline(in, line);
150       if (!in.eof()) {
151         LOG(INFO) << "cpuinfo line: " << line;
152         if (line.find("flags") != std::string::npos) {
153           LOG(INFO) << "found flags";
154           if (line.find("ssse3") != std::string::npos) {
155             has_SSSE3 = true;
156           }
157           if (line.find("sse4_1") != std::string::npos) {
158             has_SSE4_1 = true;
159           }
160           if (line.find("sse4_2") != std::string::npos) {
161             has_SSE4_2 = true;
162           }
163           if (line.find("avx") != std::string::npos) {
164             has_AVX = true;
165           }
166           if (line.find("avx2") != std::string::npos) {
167             has_AVX2 = true;
168           }
169         } else if (line.find("processor") != std::string::npos &&
170             line.find(": 1") != std::string::npos) {
171           smp = true;
172         }
173       }
174     }
175     in.close();
176   } else {
177     LOG(ERROR) << "Failed to open /proc/cpuinfo";
178   }
179   if (x86_64) {
180     return new X86_64InstructionSetFeatures(smp, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, has_AVX2);
181   } else {
182     return new X86InstructionSetFeatures(smp, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX,
183                                             has_AVX2);
184   }
185 }
186 
FromHwcap(bool x86_64)187 const X86InstructionSetFeatures* X86InstructionSetFeatures::FromHwcap(bool x86_64) {
188   UNIMPLEMENTED(WARNING);
189   return FromCppDefines(x86_64);
190 }
191 
FromAssembly(bool x86_64)192 const X86InstructionSetFeatures* X86InstructionSetFeatures::FromAssembly(bool x86_64) {
193   UNIMPLEMENTED(WARNING);
194   return FromCppDefines(x86_64);
195 }
196 
Equals(const InstructionSetFeatures * other) const197 bool X86InstructionSetFeatures::Equals(const InstructionSetFeatures* other) const {
198   if (GetInstructionSet() != other->GetInstructionSet()) {
199     return false;
200   }
201   const X86InstructionSetFeatures* other_as_x86 = other->AsX86InstructionSetFeatures();
202   return (IsSmp() == other->IsSmp()) &&
203       (has_SSSE3_ == other_as_x86->has_SSSE3_) &&
204       (has_SSE4_1_ == other_as_x86->has_SSE4_1_) &&
205       (has_SSE4_2_ == other_as_x86->has_SSE4_2_) &&
206       (has_AVX_ == other_as_x86->has_AVX_) &&
207       (has_AVX2_ == other_as_x86->has_AVX2_);
208 }
209 
AsBitmap() const210 uint32_t X86InstructionSetFeatures::AsBitmap() const {
211   return (IsSmp() ? kSmpBitfield : 0) |
212       (has_SSSE3_ ? kSsse3Bitfield : 0) |
213       (has_SSE4_1_ ? kSse4_1Bitfield : 0) |
214       (has_SSE4_2_ ? kSse4_2Bitfield : 0) |
215       (has_AVX_ ? kAvxBitfield : 0) |
216       (has_AVX2_ ? kAvx2Bitfield : 0);
217 }
218 
GetFeatureString() const219 std::string X86InstructionSetFeatures::GetFeatureString() const {
220   std::string result;
221   if (IsSmp()) {
222     result += "smp";
223   } else {
224     result += "-smp";
225   }
226   if (has_SSSE3_) {
227     result += ",ssse3";
228   } else {
229     result += ",-ssse3";
230   }
231   if (has_SSE4_1_) {
232     result += ",sse4.1";
233   } else {
234     result += ",-sse4.1";
235   }
236   if (has_SSE4_2_) {
237     result += ",sse4.2";
238   } else {
239     result += ",-sse4.2";
240   }
241   if (has_AVX_) {
242     result += ",avx";
243   } else {
244     result += ",-avx";
245   }
246   if (has_AVX2_) {
247     result += ",avx2";
248   } else {
249     result += ",-avx2";
250   }
251   return result;
252 }
253 
AddFeaturesFromSplitString(const bool smp,const std::vector<std::string> & features,bool x86_64,std::string * error_msg) const254 const InstructionSetFeatures* X86InstructionSetFeatures::AddFeaturesFromSplitString(
255     const bool smp, const std::vector<std::string>& features, bool x86_64,
256     std::string* error_msg) const {
257   bool has_SSSE3 = has_SSSE3_;
258   bool has_SSE4_1 = has_SSE4_1_;
259   bool has_SSE4_2 = has_SSE4_2_;
260   bool has_AVX = has_AVX_;
261   bool has_AVX2 = has_AVX2_;
262   for (auto i = features.begin(); i != features.end(); i++) {
263     std::string feature = Trim(*i);
264     if (feature == "ssse3") {
265       has_SSSE3 = true;
266     } else if (feature == "-ssse3") {
267       has_SSSE3 = false;
268     } else if (feature == "sse4.1") {
269       has_SSE4_1 = true;
270     } else if (feature == "-sse4.1") {
271       has_SSE4_1 = false;
272     } else if (feature == "sse4.2") {
273       has_SSE4_2 = true;
274     } else if (feature == "-sse4.2") {
275       has_SSE4_2 = false;
276     } else if (feature == "avx") {
277       has_AVX = true;
278     } else if (feature == "-avx") {
279       has_AVX = false;
280     } else if (feature == "avx2") {
281       has_AVX2 = true;
282     } else if (feature == "-avx2") {
283       has_AVX2 = false;
284     } else {
285       *error_msg = StringPrintf("Unknown instruction set feature: '%s'", feature.c_str());
286       return nullptr;
287     }
288   }
289   if (x86_64) {
290     return new X86_64InstructionSetFeatures(smp, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX,
291                                             has_AVX2);
292   } else {
293     return new X86InstructionSetFeatures(smp, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX,
294                                             has_AVX2);
295   }
296 }
297 
298 }  // namespace art
299