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