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
48const 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
78const 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
94const 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
135const 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
187const X86InstructionSetFeatures* X86InstructionSetFeatures::FromHwcap(bool x86_64) {
188  UNIMPLEMENTED(WARNING);
189  return FromCppDefines(x86_64);
190}
191
192const X86InstructionSetFeatures* X86InstructionSetFeatures::FromAssembly(bool x86_64) {
193  UNIMPLEMENTED(WARNING);
194  return FromCppDefines(x86_64);
195}
196
197bool 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
210uint32_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
219std::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
254const 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