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