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_arm64.h"
18
19 #if defined(ART_TARGET_ANDROID) && defined(__aarch64__)
20 #include <asm/hwcap.h>
21 #include <sys/auxv.h>
22 #endif
23
24 #include <fstream>
25 #include <sstream>
26
27 #include <android-base/logging.h>
28 #include <android-base/stringprintf.h>
29 #include <android-base/strings.h>
30
31 #include "base/stl_util.h"
32
33 #include <cpu_features_macros.h>
34
35 #ifdef CPU_FEATURES_ARCH_AARCH64
36 // This header can only be included on aarch64 targets,
37 // as determined by cpu_features own define.
38 #include <cpuinfo_aarch64.h>
39 #endif
40
41 namespace art {
42
43 using android::base::StringPrintf;
44
FromVariant(const std::string & variant,std::string * error_msg)45 Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromVariant(
46 const std::string& variant, std::string* error_msg) {
47 // The CPU variant string is passed to ART through --instruction-set-variant option.
48 // During build, such setting is from TARGET_CPU_VARIANT in device BoardConfig.mk, for example:
49 // TARGET_CPU_VARIANT := cortex-a75
50
51 // Look for variants that need a fix for a53 erratum 835769.
52 static const char* arm64_variants_with_a53_835769_bug[] = {
53 // Pessimistically assume all generic CPUs are cortex-a53.
54 "default",
55 "generic",
56 "cortex-a53",
57 "cortex-a53.a57",
58 "cortex-a53.a72",
59 // Pessimistically assume following "big" cortex CPUs are paired with a cortex-a53.
60 "cortex-a57",
61 "cortex-a72",
62 "cortex-a73",
63 };
64
65 static const char* arm64_variants_with_crc[] = {
66 "default",
67 "generic",
68 "cortex-a35",
69 "cortex-a53",
70 "cortex-a53.a57",
71 "cortex-a53.a72",
72 "cortex-a57",
73 "cortex-a72",
74 "cortex-a73",
75 "cortex-a55",
76 "cortex-a75",
77 "cortex-a76",
78 "exynos-m1",
79 "exynos-m2",
80 "exynos-m3",
81 "kryo",
82 "kryo385",
83 };
84
85 static const char* arm64_variants_with_lse[] = {
86 "cortex-a55",
87 "cortex-a75",
88 "cortex-a76",
89 "kryo385",
90 };
91
92 static const char* arm64_variants_with_fp16[] = {
93 "cortex-a55",
94 "cortex-a75",
95 "cortex-a76",
96 "kryo385",
97 };
98
99 static const char* arm64_variants_with_dotprod[] = {
100 "cortex-a55",
101 "cortex-a75",
102 "cortex-a76",
103 };
104
105 bool needs_a53_835769_fix = FindVariantInArray(arm64_variants_with_a53_835769_bug,
106 arraysize(arm64_variants_with_a53_835769_bug),
107 variant);
108 // The variants that need a fix for 843419 are the same that need a fix for 835769.
109 bool needs_a53_843419_fix = needs_a53_835769_fix;
110
111 bool has_crc = FindVariantInArray(arm64_variants_with_crc,
112 arraysize(arm64_variants_with_crc),
113 variant);
114
115 bool has_lse = FindVariantInArray(arm64_variants_with_lse,
116 arraysize(arm64_variants_with_lse),
117 variant);
118
119 bool has_fp16 = FindVariantInArray(arm64_variants_with_fp16,
120 arraysize(arm64_variants_with_fp16),
121 variant);
122
123 bool has_dotprod = FindVariantInArray(arm64_variants_with_dotprod,
124 arraysize(arm64_variants_with_dotprod),
125 variant);
126
127 // Currently there are no cpu variants which support SVE.
128 bool has_sve = false;
129
130 if (!needs_a53_835769_fix) {
131 // Check to see if this is an expected variant.
132 static const char* arm64_known_variants[] = {
133 "cortex-a35",
134 "cortex-a55",
135 "cortex-a75",
136 "cortex-a76",
137 "exynos-m1",
138 "exynos-m2",
139 "exynos-m3",
140 "kryo",
141 "kryo300",
142 "kryo385",
143 };
144 if (!FindVariantInArray(arm64_known_variants, arraysize(arm64_known_variants), variant)) {
145 std::ostringstream os;
146 os << "Unexpected CPU variant for Arm64: " << variant;
147 *error_msg = os.str();
148 return nullptr;
149 }
150 }
151
152 return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(needs_a53_835769_fix,
153 needs_a53_843419_fix,
154 has_crc,
155 has_lse,
156 has_fp16,
157 has_dotprod,
158 has_sve));
159 }
160
FromBitmap(uint32_t bitmap)161 Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromBitmap(uint32_t bitmap) {
162 bool is_a53 = (bitmap & kA53Bitfield) != 0;
163 bool has_crc = (bitmap & kCRCBitField) != 0;
164 bool has_lse = (bitmap & kLSEBitField) != 0;
165 bool has_fp16 = (bitmap & kFP16BitField) != 0;
166 bool has_dotprod = (bitmap & kDotProdBitField) != 0;
167 bool has_sve = (bitmap & kSVEBitField) != 0;
168 return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(is_a53,
169 is_a53,
170 has_crc,
171 has_lse,
172 has_fp16,
173 has_dotprod,
174 has_sve));
175 }
176
FromCppDefines()177 Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromCppDefines() {
178 // For more details about ARM feature macros, refer to
179 // Arm C Language Extensions Documentation (ACLE).
180 // https://developer.arm.com/docs/101028/latest
181 bool needs_a53_835769_fix = false;
182 bool needs_a53_843419_fix = needs_a53_835769_fix;
183 bool has_crc = false;
184 bool has_lse = false;
185 bool has_fp16 = false;
186 bool has_dotprod = false;
187 bool has_sve = false;
188
189 #if defined (__ARM_FEATURE_CRC32)
190 has_crc = true;
191 #endif
192
193 #if defined (__ARM_ARCH_8_1A__) || defined (__ARM_ARCH_8_2A__)
194 // There is no specific ACLE macro defined for ARMv8.1 LSE features.
195 has_lse = true;
196 #endif
197
198 #if defined (__ARM_FEATURE_FP16_SCALAR_ARITHMETIC) || defined (__ARM_FEATURE_FP16_VECTOR_ARITHMETIC)
199 has_fp16 = true;
200 #endif
201
202 #if defined (__ARM_FEATURE_DOTPROD)
203 has_dotprod = true;
204 #endif
205
206 #if defined (__ARM_FEATURE_SVE)
207 has_sve = true;
208 #endif
209
210 return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(needs_a53_835769_fix,
211 needs_a53_843419_fix,
212 has_crc,
213 has_lse,
214 has_fp16,
215 has_dotprod,
216 has_sve));
217 }
218
FromCpuInfo()219 Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromCpuInfo() {
220 UNIMPLEMENTED(WARNING);
221 return FromCppDefines();
222 }
223
FromHwcap()224 Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromHwcap() {
225 bool needs_a53_835769_fix = false; // No HWCAP for this.
226 bool needs_a53_843419_fix = false; // No HWCAP for this.
227 bool has_crc = false;
228 bool has_lse = false;
229 bool has_fp16 = false;
230 bool has_dotprod = false;
231 bool has_sve = false;
232
233 #if defined(ART_TARGET_ANDROID) && defined(__aarch64__)
234 uint64_t hwcaps = getauxval(AT_HWCAP);
235 has_crc = hwcaps & HWCAP_CRC32 ? true : false;
236 has_lse = hwcaps & HWCAP_ATOMICS ? true : false;
237 has_fp16 = hwcaps & HWCAP_FPHP ? true : false;
238 has_dotprod = hwcaps & HWCAP_ASIMDDP ? true : false;
239 has_sve = hwcaps & HWCAP_SVE ? true : false;
240 #endif
241
242 return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(needs_a53_835769_fix,
243 needs_a53_843419_fix,
244 has_crc,
245 has_lse,
246 has_fp16,
247 has_dotprod,
248 has_sve));
249 }
250
FromAssembly()251 Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromAssembly() {
252 UNIMPLEMENTED(WARNING);
253 return FromCppDefines();
254 }
255
FromCpuFeatures()256 Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromCpuFeatures() {
257 #ifdef CPU_FEATURES_ARCH_AARCH64
258 auto features = cpu_features::GetAarch64Info().features;
259 return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(false,
260 false,
261 features.crc32,
262 features.atomics,
263 features.fphp,
264 features.asimddp,
265 features.sve));
266 #else
267 UNIMPLEMENTED(WARNING);
268 return FromCppDefines();
269 #endif
270 }
271
Equals(const InstructionSetFeatures * other) const272 bool Arm64InstructionSetFeatures::Equals(const InstructionSetFeatures* other) const {
273 if (InstructionSet::kArm64 != other->GetInstructionSet()) {
274 return false;
275 }
276 const Arm64InstructionSetFeatures* other_as_arm64 = other->AsArm64InstructionSetFeatures();
277 return fix_cortex_a53_835769_ == other_as_arm64->fix_cortex_a53_835769_ &&
278 fix_cortex_a53_843419_ == other_as_arm64->fix_cortex_a53_843419_ &&
279 has_crc_ == other_as_arm64->has_crc_ &&
280 has_lse_ == other_as_arm64->has_lse_ &&
281 has_fp16_ == other_as_arm64->has_fp16_ &&
282 has_dotprod_ == other_as_arm64->has_dotprod_ &&
283 has_sve_ == other_as_arm64->has_sve_;
284 }
285
HasAtLeast(const InstructionSetFeatures * other) const286 bool Arm64InstructionSetFeatures::HasAtLeast(const InstructionSetFeatures* other) const {
287 if (InstructionSet::kArm64 != other->GetInstructionSet()) {
288 return false;
289 }
290 // Currently 'default' feature is cortex-a53 with fixes 835769 and 843419.
291 // Newer CPUs are not required to have such features,
292 // so these two a53 fix features are not tested for HasAtLeast.
293 const Arm64InstructionSetFeatures* other_as_arm64 = other->AsArm64InstructionSetFeatures();
294 return (has_crc_ || !other_as_arm64->has_crc_)
295 && (has_lse_ || !other_as_arm64->has_lse_)
296 && (has_fp16_ || !other_as_arm64->has_fp16_)
297 && (has_dotprod_ || !other_as_arm64->has_dotprod_)
298 && (has_sve_ || !other_as_arm64->has_sve_);
299 }
300
AsBitmap() const301 uint32_t Arm64InstructionSetFeatures::AsBitmap() const {
302 return (fix_cortex_a53_835769_ ? kA53Bitfield : 0)
303 | (has_crc_ ? kCRCBitField : 0)
304 | (has_lse_ ? kLSEBitField: 0)
305 | (has_fp16_ ? kFP16BitField: 0)
306 | (has_dotprod_ ? kDotProdBitField : 0)
307 | (has_sve_ ? kSVEBitField : 0);
308 }
309
GetFeatureString() const310 std::string Arm64InstructionSetFeatures::GetFeatureString() const {
311 std::string result;
312 if (fix_cortex_a53_835769_) {
313 result += "a53";
314 } else {
315 result += "-a53";
316 }
317 if (has_crc_) {
318 result += ",crc";
319 } else {
320 result += ",-crc";
321 }
322 if (has_lse_) {
323 result += ",lse";
324 } else {
325 result += ",-lse";
326 }
327 if (has_fp16_) {
328 result += ",fp16";
329 } else {
330 result += ",-fp16";
331 }
332 if (has_dotprod_) {
333 result += ",dotprod";
334 } else {
335 result += ",-dotprod";
336 }
337 if (has_sve_) {
338 result += ",sve";
339 } else {
340 result += ",-sve";
341 }
342 return result;
343 }
344
345 std::unique_ptr<const InstructionSetFeatures>
AddFeaturesFromSplitString(const std::vector<std::string> & features,std::string * error_msg) const346 Arm64InstructionSetFeatures::AddFeaturesFromSplitString(
347 const std::vector<std::string>& features, std::string* error_msg) const {
348 // This 'features' string is from '--instruction-set-features=' option in ART.
349 // These ARMv8.x feature strings align with those introduced in other compilers:
350 // https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html
351 // User can also use armv8.x-a to select group of features:
352 // armv8.1-a is equivalent to crc,lse
353 // armv8.2-a is equivalent to crc,lse,fp16
354 // armv8.3-a is equivalent to crc,lse,fp16
355 // armv8.4-a is equivalent to crc,lse,fp16,dotprod
356 // For detailed optional & mandatory features support in armv8.x-a,
357 // please refer to section 'A1.7 ARMv8 architecture extensions' in
358 // ARM Architecture Reference Manual ARMv8 document:
359 // https://developer.arm.com/products/architecture/cpu-architecture/a-profile/docs/ddi0487/latest/
360 // arm-architecture-reference-manual-armv8-for-armv8-a-architecture-profile/
361 bool is_a53 = fix_cortex_a53_835769_;
362 bool has_crc = has_crc_;
363 bool has_lse = has_lse_;
364 bool has_fp16 = has_fp16_;
365 bool has_dotprod = has_dotprod_;
366 bool has_sve = has_sve_;
367 for (const std::string& feature : features) {
368 DCHECK_EQ(android::base::Trim(feature), feature)
369 << "Feature name is not trimmed: '" << feature << "'";
370 if (feature == "a53") {
371 is_a53 = true;
372 } else if (feature == "-a53") {
373 is_a53 = false;
374 } else if (feature == "crc") {
375 has_crc = true;
376 } else if (feature == "-crc") {
377 has_crc = false;
378 } else if (feature == "lse") {
379 has_lse = true;
380 } else if (feature == "-lse") {
381 has_lse = false;
382 } else if (feature == "fp16") {
383 has_fp16 = true;
384 } else if (feature == "-fp16") {
385 has_fp16 = false;
386 } else if (feature == "dotprod") {
387 has_dotprod = true;
388 } else if (feature == "-dotprod") {
389 has_dotprod = false;
390 } else if (feature == "sve") {
391 has_sve = true;
392 } else if (feature == "-sve") {
393 has_sve = false;
394 } else if (feature == "armv8.1-a") {
395 has_crc = true;
396 has_lse = true;
397 } else if (feature == "armv8.2-a") {
398 has_crc = true;
399 has_lse = true;
400 has_fp16 = true;
401 } else if (feature == "armv8.3-a") {
402 has_crc = true;
403 has_lse = true;
404 has_fp16 = true;
405 } else if (feature == "armv8.4-a") {
406 has_crc = true;
407 has_lse = true;
408 has_fp16 = true;
409 has_dotprod = true;
410 } else {
411 *error_msg = StringPrintf("Unknown instruction set feature: '%s'", feature.c_str());
412 return nullptr;
413 }
414 }
415 return std::unique_ptr<const InstructionSetFeatures>(
416 new Arm64InstructionSetFeatures(is_a53, // erratum 835769
417 is_a53, // erratum 843419
418 has_crc,
419 has_lse,
420 has_fp16,
421 has_dotprod,
422 has_sve));
423 }
424
425 std::unique_ptr<const InstructionSetFeatures>
AddRuntimeDetectedFeatures(const InstructionSetFeatures * features) const426 Arm64InstructionSetFeatures::AddRuntimeDetectedFeatures(
427 const InstructionSetFeatures *features) const {
428 const Arm64InstructionSetFeatures *arm64_features = features->AsArm64InstructionSetFeatures();
429 return std::unique_ptr<const InstructionSetFeatures>(
430 new Arm64InstructionSetFeatures(fix_cortex_a53_835769_,
431 fix_cortex_a53_843419_,
432 arm64_features->has_crc_,
433 arm64_features->has_lse_,
434 arm64_features->has_fp16_,
435 arm64_features->has_dotprod_,
436 arm64_features->has_sve_));
437 }
438
439 } // namespace art
440