ART: Add support for ARMv8.x features for ARM64.

Add support for cortex-a76 CPU.
Add support for ARMv8.x in instruction set features.

Test: instruction_set_features_test
Test: instruction_set_features_arm64_test

Change-Id: I3ae9db34507a3bb740fc0b7ceb335486dccdf460
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc
index d0f61c9..7796ca7 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64.cc
@@ -16,6 +16,11 @@
 
 #include "instruction_set_features_arm64.h"
 
+#if defined(ART_TARGET_ANDROID) && defined(__aarch64__)
+#include <asm/hwcap.h>
+#include <sys/auxv.h>
+#endif
+
 #include <fstream>
 #include <sstream>
 
@@ -31,6 +36,10 @@
 
 Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromVariant(
     const std::string& variant, std::string* error_msg) {
+  // The CPU variant string is passed to ART through --instruction-set-variant option.
+  // During build, such setting is from TARGET_CPU_VARIANT in device BoardConfig.mk, for example:
+  //   TARGET_CPU_VARIANT := cortex-a75
+
   // Look for variants that need a fix for a53 erratum 835769.
   static const char* arm64_variants_with_a53_835769_bug[] = {
       // Pessimistically assume all generic CPUs are cortex-a53.
@@ -39,14 +48,70 @@
       "cortex-a53",
       "cortex-a53.a57",
       "cortex-a53.a72",
-      // Pessimistically assume all "big" cortex CPUs are paired with a cortex-a53.
+      // Pessimistically assume following "big" cortex CPUs are paired with a cortex-a53.
       "cortex-a57",
       "cortex-a72",
       "cortex-a73",
   };
+
+  static const char* arm64_variants_with_crc[] = {
+      "default",
+      "generic",
+      "kryo",
+      "exynos-m1",
+      "exynos-m2",
+      "exynos-m3",
+      "cortex-a35",
+      "cortex-a53",
+      "cortex-a53.a57",
+      "cortex-a53.a72",
+      "cortex-a57",
+      "cortex-a72",
+      "cortex-a73",
+      "cortex-a55",
+      "cortex-a75",
+      "cortex-a76",
+  };
+
+  static const char* arm64_variants_with_lse[] = {
+      "cortex-a55",
+      "cortex-a75",
+      "cortex-a76",
+  };
+
+  static const char* arm64_variants_with_fp16[] = {
+      "cortex-a55",
+      "cortex-a75",
+      "cortex-a76",
+  };
+
+  static const char* arm64_variants_with_dotprod[] = {
+      "cortex-a55",
+      "cortex-a75",
+      "cortex-a76",
+  };
+
   bool needs_a53_835769_fix = FindVariantInArray(arm64_variants_with_a53_835769_bug,
                                                  arraysize(arm64_variants_with_a53_835769_bug),
                                                  variant);
+  // The variants that need a fix for 843419 are the same that need a fix for 835769.
+  bool needs_a53_843419_fix = needs_a53_835769_fix;
+
+  bool has_crc = FindVariantInArray(arm64_variants_with_crc,
+                                    arraysize(arm64_variants_with_crc),
+                                    variant);
+
+  bool has_lse = FindVariantInArray(arm64_variants_with_lse,
+                                    arraysize(arm64_variants_with_lse),
+                                    variant);
+
+  bool has_fp16 = FindVariantInArray(arm64_variants_with_fp16,
+                                     arraysize(arm64_variants_with_fp16),
+                                     variant);
+
+  bool has_dotprod = FindVariantInArray(arm64_variants_with_dotprod,
+                                        arraysize(arm64_variants_with_dotprod),
+                                        variant);
 
   if (!needs_a53_835769_fix) {
     // Check to see if this is an expected variant.
@@ -54,6 +119,7 @@
         "cortex-a35",
         "cortex-a55",
         "cortex-a75",
+        "cortex-a76",
         "exynos-m1",
         "exynos-m2",
         "exynos-m3",
@@ -68,31 +134,91 @@
     }
   }
 
-  // The variants that need a fix for 843419 are the same that need a fix for 835769.
-  bool needs_a53_843419_fix = needs_a53_835769_fix;
-
-  return Arm64FeaturesUniquePtr(
-      new Arm64InstructionSetFeatures(needs_a53_835769_fix, needs_a53_843419_fix));
+  return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(needs_a53_835769_fix,
+                                                                needs_a53_843419_fix,
+                                                                has_crc,
+                                                                has_lse,
+                                                                has_fp16,
+                                                                has_dotprod));
 }
 
 Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromBitmap(uint32_t bitmap) {
   bool is_a53 = (bitmap & kA53Bitfield) != 0;
-  return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(is_a53, is_a53));
+  bool has_crc = (bitmap & kCRCBitField) != 0;
+  bool has_lse = (bitmap & kLSEBitField) != 0;
+  bool has_fp16 = (bitmap & kFP16BitField) != 0;
+  bool has_dotprod = (bitmap & kDotProdBitField) != 0;
+  return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(is_a53,
+                                                                is_a53,
+                                                                has_crc,
+                                                                has_lse,
+                                                                has_fp16,
+                                                                has_dotprod));
 }
 
 Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromCppDefines() {
-  const bool is_a53 = true;  // Pessimistically assume all ARM64s are A53s.
-  return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(is_a53, is_a53));
+  // For more details about ARM feature macros, refer to
+  // Arm C Language Extensions Documentation (ACLE).
+  // https://developer.arm.com/docs/101028/latest
+  bool needs_a53_835769_fix = false;
+  bool needs_a53_843419_fix = needs_a53_835769_fix;
+  bool has_crc = false;
+  bool has_lse = false;
+  bool has_fp16 = false;
+  bool has_dotprod = false;
+
+#if defined (__ARM_FEATURE_CRC32)
+  has_crc = true;
+#endif
+
+#if defined (__ARM_ARCH_8_1A__) || defined (__ARM_ARCH_8_2A__)
+  // There is no specific ACLE macro defined for ARMv8.1 LSE features.
+  has_lse = true;
+#endif
+
+#if defined (__ARM_FEATURE_FP16_SCALAR_ARITHMETIC) || defined (__ARM_FEATURE_FP16_VECTOR_ARITHMETIC)
+  has_fp16 = true;
+#endif
+
+#if defined (__ARM_FEATURE_DOTPROD)
+  has_dotprod = true;
+#endif
+
+  return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(needs_a53_835769_fix,
+                                                                needs_a53_843419_fix,
+                                                                has_crc,
+                                                                has_lse,
+                                                                has_fp16,
+                                                                has_dotprod));
 }
 
 Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromCpuInfo() {
-  const bool is_a53 = true;  // Conservative default.
-  return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(is_a53, is_a53));
+  UNIMPLEMENTED(WARNING);
+  return FromCppDefines();
 }
 
 Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromHwcap() {
-  const bool is_a53 = true;  // Pessimistically assume all ARM64s are A53s.
-  return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(is_a53, is_a53));
+  bool needs_a53_835769_fix = false;  // No HWCAP for this.
+  bool needs_a53_843419_fix = false;  // No HWCAP for this.
+  bool has_crc = false;
+  bool has_lse = false;
+  bool has_fp16 = false;
+  bool has_dotprod = false;
+
+#if defined(ART_TARGET_ANDROID) && defined(__aarch64__)
+  uint64_t hwcaps = getauxval(AT_HWCAP);
+  has_crc = hwcaps & HWCAP_CRC32 ? true : false;
+  has_lse = hwcaps & HWCAP_ATOMICS ? true : false;
+  has_fp16 = hwcaps & HWCAP_FPHP ? true : false;
+  has_dotprod = hwcaps & HWCAP_ASIMDDP ? true : false;
+#endif
+
+  return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(needs_a53_835769_fix,
+                                                                needs_a53_843419_fix,
+                                                                has_crc,
+                                                                has_lse,
+                                                                has_fp16,
+                                                                has_dotprod));
 }
 
 Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromAssembly() {
@@ -106,11 +232,33 @@
   }
   const Arm64InstructionSetFeatures* other_as_arm64 = other->AsArm64InstructionSetFeatures();
   return fix_cortex_a53_835769_ == other_as_arm64->fix_cortex_a53_835769_ &&
-      fix_cortex_a53_843419_ == other_as_arm64->fix_cortex_a53_843419_;
+      fix_cortex_a53_843419_ == other_as_arm64->fix_cortex_a53_843419_ &&
+      has_crc_ == other_as_arm64->has_crc_ &&
+      has_lse_ == other_as_arm64->has_lse_ &&
+      has_fp16_ == other_as_arm64->has_fp16_ &&
+      has_dotprod_ == other_as_arm64->has_dotprod_;
+}
+
+bool Arm64InstructionSetFeatures::HasAtLeast(const InstructionSetFeatures* other) const {
+  if (InstructionSet::kArm64 != other->GetInstructionSet()) {
+    return false;
+  }
+  // Currently 'default' feature is cortex-a53 with fixes 835769 and 843419.
+  // Newer CPUs are not required to have such features,
+  // so these two a53 fix features are not tested for HasAtLeast.
+  const Arm64InstructionSetFeatures* other_as_arm64 = other->AsArm64InstructionSetFeatures();
+  return (has_crc_ || !other_as_arm64->has_crc_)
+      && (has_lse_ || !other_as_arm64->has_lse_)
+      && (has_fp16_ || !other_as_arm64->has_fp16_)
+      && (has_dotprod_ || !other_as_arm64->has_dotprod_);
 }
 
 uint32_t Arm64InstructionSetFeatures::AsBitmap() const {
-  return (fix_cortex_a53_835769_ ? kA53Bitfield : 0);
+  return (fix_cortex_a53_835769_ ? kA53Bitfield : 0)
+      | (has_crc_ ? kCRCBitField : 0)
+      | (has_lse_ ? kLSEBitField: 0)
+      | (has_fp16_ ? kFP16BitField: 0)
+      | (has_dotprod_ ? kDotProdBitField : 0);
 }
 
 std::string Arm64InstructionSetFeatures::GetFeatureString() const {
@@ -120,26 +268,100 @@
   } else {
     result += "-a53";
   }
+  if (has_crc_) {
+    result += ",crc";
+  } else {
+    result += ",-crc";
+  }
+  if (has_lse_) {
+    result += ",lse";
+  } else {
+    result += ",-lse";
+  }
+  if (has_fp16_) {
+    result += ",fp16";
+  } else {
+    result += ",-fp16";
+  }
+  if (has_dotprod_) {
+    result += ",dotprod";
+  } else {
+    result += ",-dotprod";
+  }
   return result;
 }
 
 std::unique_ptr<const InstructionSetFeatures>
 Arm64InstructionSetFeatures::AddFeaturesFromSplitString(
     const std::vector<std::string>& features, std::string* error_msg) const {
+  // This 'features' string is from '--instruction-set-features=' option in ART.
+  // These ARMv8.x feature strings align with those introduced in other compilers:
+  // https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html
+  // User can also use armv8.x-a to select group of features:
+  //   armv8.1-a is equivalent to crc,lse
+  //   armv8.2-a is equivalent to crc,lse,fp16
+  //   armv8.3-a is equivalent to crc,lse,fp16
+  //   armv8.4-a is equivalent to crc,lse,fp16,dotprod
+  // For detailed optional & mandatory features support in armv8.x-a,
+  // please refer to section 'A1.7 ARMv8 architecture extensions' in
+  // ARM Architecture Reference Manual ARMv8 document:
+  // https://developer.arm.com/products/architecture/cpu-architecture/a-profile/docs/ddi0487/latest/
+  // arm-architecture-reference-manual-armv8-for-armv8-a-architecture-profile/
   bool is_a53 = fix_cortex_a53_835769_;
+  bool has_crc = has_crc_;
+  bool has_lse = has_lse_;
+  bool has_fp16 = has_fp16_;
+  bool has_dotprod = has_dotprod_;
   for (auto i = features.begin(); i != features.end(); i++) {
     std::string feature = android::base::Trim(*i);
     if (feature == "a53") {
       is_a53 = true;
     } else if (feature == "-a53") {
       is_a53 = false;
+    } else if (feature == "crc") {
+      has_crc = true;
+    } else if (feature == "-crc") {
+      has_crc = false;
+    } else if (feature == "lse") {
+      has_lse = true;
+    } else if (feature == "-lse") {
+      has_lse = false;
+    } else if (feature == "fp16") {
+      has_fp16 = true;
+    } else if (feature == "-fp16") {
+      has_fp16 = false;
+    } else if (feature == "dotprod") {
+      has_dotprod = true;
+    } else if (feature == "-dotprod") {
+      has_dotprod = false;
+    } else if (feature == "armv8.1-a") {
+      has_crc = true;
+      has_lse = true;
+    } else if (feature == "armv8.2-a") {
+      has_crc = true;
+      has_lse = true;
+      has_fp16 = true;
+    } else if (feature == "armv8.3-a") {
+      has_crc = true;
+      has_lse = true;
+      has_fp16 = true;
+    } else if (feature == "armv8.4-a") {
+      has_crc = true;
+      has_lse = true;
+      has_fp16 = true;
+      has_dotprod = true;
     } else {
       *error_msg = StringPrintf("Unknown instruction set feature: '%s'", feature.c_str());
       return nullptr;
     }
   }
   return std::unique_ptr<const InstructionSetFeatures>(
-      new Arm64InstructionSetFeatures(is_a53, is_a53));
+      new Arm64InstructionSetFeatures(is_a53,  // erratum 835769
+                                      is_a53,  // erratum 843419
+                                      has_crc,
+                                      has_lse,
+                                      has_fp16,
+                                      has_dotprod));
 }
 
 }  // namespace art
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.h b/runtime/arch/arm64/instruction_set_features_arm64.h
index 163a2d8..4ec8fa2 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.h
+++ b/runtime/arch/arm64/instruction_set_features_arm64.h
@@ -49,6 +49,11 @@
 
   bool Equals(const InstructionSetFeatures* other) const override;
 
+  // Note that newer CPUs do not have a53 erratum 835769 and 843419,
+  // so the two a53 fix features (fix_cortex_a53_835769 and fix_cortex_a53_843419)
+  // are not tested for HasAtLeast.
+  bool HasAtLeast(const InstructionSetFeatures* other) const override;
+
   InstructionSet GetInstructionSet() const override {
     return InstructionSet::kArm64;
   }
@@ -68,6 +73,23 @@
       return fix_cortex_a53_843419_;
   }
 
+  bool HasCRC() const {
+    return has_crc_;
+  }
+
+  bool HasLSE() const {
+    return has_lse_;
+  }
+
+  bool HasFP16() const {
+    return has_fp16_;
+  }
+
+  // Are Dot Product instructions (UDOT/SDOT) available?
+  bool HasDotProd() const {
+    return has_dotprod_;
+  }
+
   virtual ~Arm64InstructionSetFeatures() {}
 
  protected:
@@ -77,19 +99,36 @@
                                  std::string* error_msg) const override;
 
  private:
-  Arm64InstructionSetFeatures(bool needs_a53_835769_fix, bool needs_a53_843419_fix)
+  Arm64InstructionSetFeatures(bool needs_a53_835769_fix,
+                              bool needs_a53_843419_fix,
+                              bool has_crc,
+                              bool has_lse,
+                              bool has_fp16,
+                              bool has_dotprod)
       : InstructionSetFeatures(),
         fix_cortex_a53_835769_(needs_a53_835769_fix),
-        fix_cortex_a53_843419_(needs_a53_843419_fix) {
+        fix_cortex_a53_843419_(needs_a53_843419_fix),
+        has_crc_(has_crc),
+        has_lse_(has_lse),
+        has_fp16_(has_fp16),
+        has_dotprod_(has_dotprod) {
   }
 
   // Bitmap positions for encoding features as a bitmap.
   enum {
     kA53Bitfield = 1 << 0,
+    kCRCBitField = 1 << 1,
+    kLSEBitField = 1 << 2,
+    kFP16BitField = 1 << 3,
+    kDotProdBitField = 1 << 4,
   };
 
   const bool fix_cortex_a53_835769_;
   const bool fix_cortex_a53_843419_;
+  const bool has_crc_;      // optional in ARMv8.0, mandatory in ARMv8.1.
+  const bool has_lse_;      // ARMv8.1 Large System Extensions.
+  const bool has_fp16_;     // ARMv8.2 FP16 extensions.
+  const bool has_dotprod_;  // optional in ARMv8.2, mandatory in ARMv8.4.
 
   DISALLOW_COPY_AND_ASSIGN(Arm64InstructionSetFeatures);
 };
diff --git a/runtime/arch/arm64/instruction_set_features_arm64_test.cc b/runtime/arch/arm64/instruction_set_features_arm64_test.cc
index b946f4f..99d6b0d 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64_test.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64_test.cc
@@ -28,32 +28,37 @@
   ASSERT_TRUE(arm64_features.get() != nullptr) << error_msg;
   EXPECT_EQ(arm64_features->GetInstructionSet(), InstructionSet::kArm64);
   EXPECT_TRUE(arm64_features->Equals(arm64_features.get()));
-  EXPECT_STREQ("a53", arm64_features->GetFeatureString().c_str());
-  EXPECT_EQ(arm64_features->AsBitmap(), 1U);
+  EXPECT_STREQ("a53,crc,-lse,-fp16,-dotprod", arm64_features->GetFeatureString().c_str());
+  EXPECT_EQ(arm64_features->AsBitmap(), 3U);
 
   std::unique_ptr<const InstructionSetFeatures> cortex_a57_features(
       InstructionSetFeatures::FromVariant(InstructionSet::kArm64, "cortex-a57", &error_msg));
   ASSERT_TRUE(cortex_a57_features.get() != nullptr) << error_msg;
   EXPECT_EQ(cortex_a57_features->GetInstructionSet(), InstructionSet::kArm64);
   EXPECT_TRUE(cortex_a57_features->Equals(cortex_a57_features.get()));
-  EXPECT_STREQ("a53", cortex_a57_features->GetFeatureString().c_str());
-  EXPECT_EQ(cortex_a57_features->AsBitmap(), 1U);
+  EXPECT_TRUE(cortex_a57_features->HasAtLeast(arm64_features.get()));
+  EXPECT_STREQ("a53,crc,-lse,-fp16,-dotprod", cortex_a57_features->GetFeatureString().c_str());
+  EXPECT_EQ(cortex_a57_features->AsBitmap(), 3U);
 
   std::unique_ptr<const InstructionSetFeatures> cortex_a73_features(
       InstructionSetFeatures::FromVariant(InstructionSet::kArm64, "cortex-a73", &error_msg));
   ASSERT_TRUE(cortex_a73_features.get() != nullptr) << error_msg;
   EXPECT_EQ(cortex_a73_features->GetInstructionSet(), InstructionSet::kArm64);
   EXPECT_TRUE(cortex_a73_features->Equals(cortex_a73_features.get()));
-  EXPECT_STREQ("a53", cortex_a73_features->GetFeatureString().c_str());
-  EXPECT_EQ(cortex_a73_features->AsBitmap(), 1U);
+  EXPECT_TRUE(cortex_a73_features->AsArm64InstructionSetFeatures()->HasCRC());
+  EXPECT_FALSE(cortex_a73_features->AsArm64InstructionSetFeatures()->HasLSE());
+  EXPECT_FALSE(cortex_a73_features->AsArm64InstructionSetFeatures()->HasFP16());
+  EXPECT_FALSE(cortex_a73_features->AsArm64InstructionSetFeatures()->HasDotProd());
+  EXPECT_STREQ("a53,crc,-lse,-fp16,-dotprod", cortex_a73_features->GetFeatureString().c_str());
+  EXPECT_EQ(cortex_a73_features->AsBitmap(), 3U);
 
   std::unique_ptr<const InstructionSetFeatures> cortex_a35_features(
       InstructionSetFeatures::FromVariant(InstructionSet::kArm64, "cortex-a35", &error_msg));
   ASSERT_TRUE(cortex_a35_features.get() != nullptr) << error_msg;
   EXPECT_EQ(cortex_a35_features->GetInstructionSet(), InstructionSet::kArm64);
   EXPECT_TRUE(cortex_a35_features->Equals(cortex_a35_features.get()));
-  EXPECT_STREQ("-a53", cortex_a35_features->GetFeatureString().c_str());
-  EXPECT_EQ(cortex_a35_features->AsBitmap(), 0U);
+  EXPECT_STREQ("-a53,crc,-lse,-fp16,-dotprod", cortex_a35_features->GetFeatureString().c_str());
+  EXPECT_EQ(cortex_a35_features->AsBitmap(), 2U);
 
   std::unique_ptr<const InstructionSetFeatures> kryo_features(
       InstructionSetFeatures::FromVariant(InstructionSet::kArm64, "kryo", &error_msg));
@@ -62,28 +67,107 @@
   EXPECT_TRUE(kryo_features->Equals(kryo_features.get()));
   EXPECT_TRUE(kryo_features->Equals(cortex_a35_features.get()));
   EXPECT_FALSE(kryo_features->Equals(cortex_a57_features.get()));
-  EXPECT_STREQ("-a53", kryo_features->GetFeatureString().c_str());
-  EXPECT_EQ(kryo_features->AsBitmap(), 0U);
+  EXPECT_STREQ("-a53,crc,-lse,-fp16,-dotprod", kryo_features->GetFeatureString().c_str());
+  EXPECT_EQ(kryo_features->AsBitmap(), 2U);
 
   std::unique_ptr<const InstructionSetFeatures> cortex_a55_features(
       InstructionSetFeatures::FromVariant(InstructionSet::kArm64, "cortex-a55", &error_msg));
   ASSERT_TRUE(cortex_a55_features.get() != nullptr) << error_msg;
   EXPECT_EQ(cortex_a55_features->GetInstructionSet(), InstructionSet::kArm64);
   EXPECT_TRUE(cortex_a55_features->Equals(cortex_a55_features.get()));
-  EXPECT_TRUE(cortex_a55_features->Equals(cortex_a35_features.get()));
+  EXPECT_FALSE(cortex_a55_features->Equals(cortex_a35_features.get()));
   EXPECT_FALSE(cortex_a55_features->Equals(cortex_a57_features.get()));
-  EXPECT_STREQ("-a53", cortex_a55_features->GetFeatureString().c_str());
-  EXPECT_EQ(cortex_a55_features->AsBitmap(), 0U);
+  EXPECT_TRUE(cortex_a35_features->HasAtLeast(arm64_features.get()));
+  EXPECT_STREQ("-a53,crc,lse,fp16,dotprod", cortex_a55_features->GetFeatureString().c_str());
+  EXPECT_EQ(cortex_a55_features->AsBitmap(), 30U);
 
   std::unique_ptr<const InstructionSetFeatures> cortex_a75_features(
       InstructionSetFeatures::FromVariant(InstructionSet::kArm64, "cortex-a75", &error_msg));
   ASSERT_TRUE(cortex_a75_features.get() != nullptr) << error_msg;
   EXPECT_EQ(cortex_a75_features->GetInstructionSet(), InstructionSet::kArm64);
   EXPECT_TRUE(cortex_a75_features->Equals(cortex_a75_features.get()));
-  EXPECT_TRUE(cortex_a75_features->Equals(cortex_a35_features.get()));
+  EXPECT_FALSE(cortex_a75_features->Equals(cortex_a35_features.get()));
   EXPECT_FALSE(cortex_a75_features->Equals(cortex_a57_features.get()));
-  EXPECT_STREQ("-a53", cortex_a75_features->GetFeatureString().c_str());
-  EXPECT_EQ(cortex_a75_features->AsBitmap(), 0U);
+  EXPECT_TRUE(cortex_a75_features->HasAtLeast(arm64_features.get()));
+  EXPECT_TRUE(cortex_a75_features->HasAtLeast(cortex_a55_features.get()));
+  EXPECT_FALSE(cortex_a35_features->HasAtLeast(cortex_a75_features.get()));
+  EXPECT_FALSE(cortex_a75_features->AsArm64InstructionSetFeatures()->NeedFixCortexA53_835769());
+  EXPECT_FALSE(cortex_a75_features->AsArm64InstructionSetFeatures()->NeedFixCortexA53_843419());
+  EXPECT_TRUE(cortex_a75_features->AsArm64InstructionSetFeatures()->HasCRC());
+  EXPECT_TRUE(cortex_a75_features->AsArm64InstructionSetFeatures()->HasLSE());
+  EXPECT_TRUE(cortex_a75_features->AsArm64InstructionSetFeatures()->HasFP16());
+  EXPECT_TRUE(cortex_a75_features->AsArm64InstructionSetFeatures()->HasDotProd());
+  EXPECT_STREQ("-a53,crc,lse,fp16,dotprod", cortex_a75_features->GetFeatureString().c_str());
+  EXPECT_EQ(cortex_a75_features->AsBitmap(), 30U);
+
+  std::unique_ptr<const InstructionSetFeatures> cortex_a76_features(
+      InstructionSetFeatures::FromVariant(InstructionSet::kArm64, "cortex-a76", &error_msg));
+  ASSERT_TRUE(cortex_a76_features.get() != nullptr) << error_msg;
+  EXPECT_EQ(cortex_a76_features->GetInstructionSet(), InstructionSet::kArm64);
+  EXPECT_TRUE(cortex_a76_features->Equals(cortex_a76_features.get()));
+  EXPECT_FALSE(cortex_a76_features->Equals(cortex_a35_features.get()));
+  EXPECT_FALSE(cortex_a76_features->Equals(cortex_a57_features.get()));
+  EXPECT_TRUE(cortex_a76_features->Equals(cortex_a75_features.get()));
+  EXPECT_TRUE(cortex_a76_features->HasAtLeast(arm64_features.get()));
+  EXPECT_TRUE(cortex_a76_features->HasAtLeast(cortex_a55_features.get()));
+  EXPECT_FALSE(cortex_a35_features->HasAtLeast(cortex_a76_features.get()));
+  EXPECT_FALSE(cortex_a76_features->AsArm64InstructionSetFeatures()->NeedFixCortexA53_835769());
+  EXPECT_FALSE(cortex_a76_features->AsArm64InstructionSetFeatures()->NeedFixCortexA53_843419());
+  EXPECT_TRUE(cortex_a76_features->AsArm64InstructionSetFeatures()->HasCRC());
+  EXPECT_TRUE(cortex_a76_features->AsArm64InstructionSetFeatures()->HasLSE());
+  EXPECT_TRUE(cortex_a76_features->AsArm64InstructionSetFeatures()->HasFP16());
+  EXPECT_TRUE(cortex_a76_features->AsArm64InstructionSetFeatures()->HasDotProd());
+  EXPECT_STREQ("-a53,crc,lse,fp16,dotprod", cortex_a76_features->GetFeatureString().c_str());
+  EXPECT_EQ(cortex_a76_features->AsBitmap(), 30U);
+}
+
+TEST(Arm64InstructionSetFeaturesTest, Arm64AddFeaturesFromString) {
+  std::string error_msg;
+  std::unique_ptr<const InstructionSetFeatures> base_features(
+      InstructionSetFeatures::FromVariant(InstructionSet::kArm64, "generic", &error_msg));
+  ASSERT_TRUE(base_features.get() != nullptr) << error_msg;
+
+  // Build features for a Cortex-A76 processor (with ARMv8.2 and Dot Product exentions support).
+  std::unique_ptr<const InstructionSetFeatures> a76_features(
+      base_features->AddFeaturesFromString("-a53,armv8.2-a,dotprod", &error_msg));
+  ASSERT_TRUE(a76_features.get() != nullptr) << error_msg;
+  ASSERT_EQ(a76_features->GetInstructionSet(), InstructionSet::kArm64);
+  EXPECT_TRUE(a76_features->Equals(a76_features.get()));
+  EXPECT_FALSE(a76_features->AsArm64InstructionSetFeatures()->NeedFixCortexA53_835769());
+  EXPECT_FALSE(a76_features->AsArm64InstructionSetFeatures()->NeedFixCortexA53_843419());
+  EXPECT_TRUE(a76_features->AsArm64InstructionSetFeatures()->HasCRC());
+  EXPECT_TRUE(a76_features->AsArm64InstructionSetFeatures()->HasLSE());
+  EXPECT_TRUE(a76_features->AsArm64InstructionSetFeatures()->HasFP16());
+  EXPECT_TRUE(a76_features->AsArm64InstructionSetFeatures()->HasDotProd());
+  EXPECT_STREQ("-a53,crc,lse,fp16,dotprod", a76_features->GetFeatureString().c_str());
+  EXPECT_EQ(a76_features->AsBitmap(), 30U);
+
+  // Build features for a default ARM64 processor.
+  std::unique_ptr<const InstructionSetFeatures> generic_features(
+      base_features->AddFeaturesFromString("default", &error_msg));
+  ASSERT_TRUE(generic_features.get() != nullptr) << error_msg;
+  ASSERT_EQ(generic_features->GetInstructionSet(), InstructionSet::kArm64);
+  EXPECT_TRUE(generic_features->Equals(generic_features.get()));
+  EXPECT_FALSE(generic_features->AsArm64InstructionSetFeatures()->HasLSE());
+  EXPECT_FALSE(generic_features->AsArm64InstructionSetFeatures()->HasFP16());
+  EXPECT_FALSE(generic_features->AsArm64InstructionSetFeatures()->HasDotProd());
+  EXPECT_STREQ("a53,crc,-lse,-fp16,-dotprod", generic_features->GetFeatureString().c_str());
+  EXPECT_EQ(generic_features->AsBitmap(), 3U);
+
+  // Build features for a ARM64 processor that supports up to ARMv8.2.
+  std::unique_ptr<const InstructionSetFeatures> armv8_2a_cpu_features(
+      base_features->AddFeaturesFromString("-a53,armv8.2-a", &error_msg));
+  ASSERT_TRUE(armv8_2a_cpu_features.get() != nullptr) << error_msg;
+  ASSERT_EQ(armv8_2a_cpu_features->GetInstructionSet(), InstructionSet::kArm64);
+  EXPECT_TRUE(armv8_2a_cpu_features->Equals(armv8_2a_cpu_features.get()));
+  EXPECT_FALSE(armv8_2a_cpu_features->AsArm64InstructionSetFeatures()->NeedFixCortexA53_835769());
+  EXPECT_FALSE(armv8_2a_cpu_features->AsArm64InstructionSetFeatures()->NeedFixCortexA53_843419());
+  EXPECT_TRUE(armv8_2a_cpu_features->AsArm64InstructionSetFeatures()->HasCRC());
+  EXPECT_TRUE(armv8_2a_cpu_features->AsArm64InstructionSetFeatures()->HasLSE());
+  EXPECT_TRUE(armv8_2a_cpu_features->AsArm64InstructionSetFeatures()->HasFP16());
+  EXPECT_FALSE(armv8_2a_cpu_features->AsArm64InstructionSetFeatures()->HasDotProd());
+  EXPECT_STREQ("-a53,crc,lse,fp16,-dotprod", armv8_2a_cpu_features->GetFeatureString().c_str());
+  EXPECT_EQ(armv8_2a_cpu_features->AsBitmap(), 14U);
 }
 
 }  // namespace art