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