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