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
26namespace art {
27
28// Feature-support arrays.
29
30static constexpr const char* x86_known_variants[] = {
31    "atom",
32    "silvermont",
33};
34
35static constexpr const char* x86_variants_with_ssse3[] = {
36    "atom",
37    "silvermont",
38};
39
40static constexpr const char* x86_variants_with_sse4_1[] = {
41    "silvermont",
42};
43
44static constexpr const char* x86_variants_with_sse4_2[] = {
45    "silvermont",
46};
47
48static constexpr const char* x86_variants_prefer_locked_add_sync[] = {
49    "atom",
50    "silvermont",
51};
52
53static constexpr const char* x86_variants_with_popcnt[] = {
54    "silvermont",
55};
56
57const 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
96const 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
117const 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
168const 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
227const X86InstructionSetFeatures* X86InstructionSetFeatures::FromHwcap(bool x86_64) {
228  UNIMPLEMENTED(WARNING);
229  return FromCppDefines(x86_64);
230}
231
232const X86InstructionSetFeatures* X86InstructionSetFeatures::FromAssembly(bool x86_64) {
233  UNIMPLEMENTED(WARNING);
234  return FromCppDefines(x86_64);
235}
236
237bool 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
252uint32_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
263std::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
308const 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