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