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