Merge "ARM: Update `ArmInstructionSetFeatures` to track ARMv8-A."
diff --git a/runtime/arch/arm/instruction_set_features_arm.cc b/runtime/arch/arm/instruction_set_features_arm.cc
index 6c2c815..8384460 100644
--- a/runtime/arch/arm/instruction_set_features_arm.cc
+++ b/runtime/arch/arm/instruction_set_features_arm.cc
@@ -31,6 +31,7 @@
 
 #if defined(__arm__)
 extern "C" bool artCheckForArmSdivInstruction();
+extern "C" bool artCheckForArmv8AInstructions();
 #endif
 
 namespace art {
@@ -39,22 +40,34 @@
 
 ArmFeaturesUniquePtr ArmInstructionSetFeatures::FromVariant(
     const std::string& variant, std::string* error_msg) {
+  static const char* arm_variants_with_armv8a[] = {
+      "cortex-a32",
+      "cortex-a35",
+      "cortex-a53",
+      "cortex-a53.a57",
+      "cortex-a53.a72",
+      "cortex-a57",
+      "cortex-a72",
+      "cortex-a73",
+      "exynos-m1",
+      "denver",
+      "kryo"
+  };
+  bool has_armv8a = FindVariantInArray(arm_variants_with_armv8a,
+                                       arraysize(arm_variants_with_armv8a),
+                                       variant);
+
   // Look for variants that have divide support.
   static const char* arm_variants_with_div[] = {
       "cortex-a7",
       "cortex-a12",
       "cortex-a15",
       "cortex-a17",
-      "cortex-a53",
-      "cortex-a53.a57",
-      "cortex-a57",
-      "denver",
       "krait",
   };
-
-  bool has_div = FindVariantInArray(arm_variants_with_div,
-                                    arraysize(arm_variants_with_div),
-                                    variant);
+  bool has_div = has_armv8a || FindVariantInArray(arm_variants_with_div,
+                                                  arraysize(arm_variants_with_div),
+                                                  variant);
 
   // Look for variants that have LPAE support.
   static const char* arm_variants_with_lpae[] = {
@@ -62,17 +75,13 @@
       "cortex-a12",
       "cortex-a15",
       "cortex-a17",
-      "cortex-a53",
-      "cortex-a53.a57",
-      "cortex-a57",
-      "denver",
       "krait",
   };
-  bool has_lpae = FindVariantInArray(arm_variants_with_lpae,
-                                     arraysize(arm_variants_with_lpae),
-                                     variant);
+  bool has_atomic_ldrd_strd = has_armv8a || FindVariantInArray(arm_variants_with_lpae,
+                                                               arraysize(arm_variants_with_lpae),
+                                                               variant);
 
-  if (has_div == false && has_lpae == false) {
+  if (has_armv8a == false && has_div == false && has_atomic_ldrd_strd == false) {
     static const char* arm_variants_with_default_features[] = {
         "cortex-a5",
         "cortex-a8",
@@ -92,34 +101,48 @@
           << ") using conservative defaults";
     }
   }
-  return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(has_div, has_lpae));
+  return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(has_div,
+                                                            has_atomic_ldrd_strd,
+                                                            has_armv8a));
 }
 
 ArmFeaturesUniquePtr ArmInstructionSetFeatures::FromBitmap(uint32_t bitmap) {
   bool has_div = (bitmap & kDivBitfield) != 0;
   bool has_atomic_ldrd_strd = (bitmap & kAtomicLdrdStrdBitfield) != 0;
-  return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(has_div, has_atomic_ldrd_strd));
+  bool has_armv8a = (bitmap & kARMv8A) != 0;
+  return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(has_div,
+                                                            has_atomic_ldrd_strd,
+                                                            has_armv8a));
 }
 
 ArmFeaturesUniquePtr ArmInstructionSetFeatures::FromCppDefines() {
-#if defined(__ARM_ARCH_EXT_IDIV__)
+// Note: This will not work for now since we still build the 32-bit as __ARCH_ARM_7A__.
+#if defined(__ARM_ARCH_8A__)
+  const bool has_armv8a = true;
+#else
+  const bool has_armv8a = false;
+#endif
+#if defined (__ARM_ARCH_8A__) || defined(__ARM_ARCH_EXT_IDIV__)
   const bool has_div = true;
 #else
   const bool has_div = false;
 #endif
-#if defined(__ARM_FEATURE_LPAE)
-  const bool has_lpae = true;
+#if defined (__ARM_ARCH_8A__) || defined(__ARM_FEATURE_LPAE)
+  const bool has_atomic_ldrd_strd = true;
 #else
-  const bool has_lpae = false;
+  const bool has_atomic_ldrd_strd = false;
 #endif
-  return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(has_div, has_lpae));
+  return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(has_div,
+                                                            has_atomic_ldrd_strd,
+                                                            has_armv8a));
 }
 
 ArmFeaturesUniquePtr ArmInstructionSetFeatures::FromCpuInfo() {
   // Look in /proc/cpuinfo for features we need.  Only use this when we can guarantee that
   // the kernel puts the appropriate feature flags in here.  Sometimes it doesn't.
-  bool has_lpae = false;
+  bool has_atomic_ldrd_strd = false;
   bool has_div = false;
+  bool has_armv8a = false;
 
   std::ifstream in("/proc/cpuinfo");
   if (!in.fail()) {
@@ -137,21 +160,33 @@
             has_div = true;
           }
           if (line.find("lpae") != std::string::npos) {
-            has_lpae = true;
+            has_atomic_ldrd_strd = true;
           }
         }
+        if (line.find("architecture") != std::string::npos
+            && line.find(": 8") != std::string::npos) {
+          LOG(INFO) << "found architecture ARMv8";
+          // Android is only run on A cores, so ARMv8 implies ARMv8-A.
+          has_armv8a = true;
+          // ARMv8 CPUs have LPAE and div support.
+          has_div = true;
+          has_atomic_ldrd_strd = true;
+        }
       }
     }
     in.close();
   } else {
     LOG(ERROR) << "Failed to open /proc/cpuinfo";
   }
-  return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(has_div, has_lpae));
+  return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(has_div,
+                                                            has_atomic_ldrd_strd,
+                                                            has_armv8a));
 }
 
 ArmFeaturesUniquePtr ArmInstructionSetFeatures::FromHwcap() {
   bool has_div = false;
-  bool has_lpae = false;
+  bool has_atomic_ldrd_strd = false;
+  bool has_armv8a = false;
 
 #if defined(ART_TARGET_ANDROID) && defined(__arm__)
   uint64_t hwcaps = getauxval(AT_HWCAP);
@@ -163,18 +198,27 @@
     has_div = true;
   }
   if ((hwcaps & HWCAP_LPAE) != 0) {
-    has_lpae = true;
+    has_atomic_ldrd_strd = true;
+  }
+  // TODO: Fix this once FPMISC makes it upstream.
+  // For now we detect if we run on an ARMv8 CPU by looking for CRC32 and SHA1
+  // (only available on ARMv8 CPUs).
+  if ((hwcaps & HWCAP2_CRC32) != 0 && (hwcaps & HWCAP2_SHA1) != 0) {
+    has_armv8a = true;
   }
 #endif
 
-  return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(has_div, has_lpae));
+  return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(has_div,
+                                                            has_atomic_ldrd_strd,
+                                                            has_armv8a));
 }
 
 // A signal handler called by a fault for an illegal instruction.  We record the fact in r0
 // and then increment the PC in the signal context to return to the next instruction.  We know the
-// instruction is an sdiv (4 bytes long).
-static void bad_divide_inst_handle(int signo ATTRIBUTE_UNUSED, siginfo_t* si ATTRIBUTE_UNUSED,
-                                   void* data) {
+// instruction is 4 bytes long.
+static void bad_instr_handle(int signo ATTRIBUTE_UNUSED,
+                            siginfo_t* si ATTRIBUTE_UNUSED,
+                            void* data) {
 #if defined(__arm__)
   struct ucontext *uc = (struct ucontext *)data;
   struct sigcontext *sc = &uc->uc_mcontext;
@@ -190,15 +234,19 @@
   // instruction.  If we get a SIGILL then it's not supported.
   struct sigaction sa, osa;
   sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO;
-  sa.sa_sigaction = bad_divide_inst_handle;
+  sa.sa_sigaction = bad_instr_handle;
   sigemptyset(&sa.sa_mask);
   sigaction(SIGILL, &sa, &osa);
 
   bool has_div = false;
+  bool has_armv8a = false;
 #if defined(__arm__)
   if (artCheckForArmSdivInstruction()) {
     has_div = true;
   }
+  if (artCheckForArmv8AInstructions()) {
+    has_armv8a = true;
+  }
 #endif
 
   // Restore the signal handler.
@@ -207,11 +255,13 @@
   // Use compile time features to "detect" LPAE support.
   // TODO: write an assembly LPAE support test.
 #if defined(__ARM_FEATURE_LPAE)
-  const bool has_lpae = true;
+  const bool has_atomic_ldrd_strd = true;
 #else
-  const bool has_lpae = false;
+  const bool has_atomic_ldrd_strd = false;
 #endif
-  return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(has_div, has_lpae));
+  return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(has_div,
+                                                            has_atomic_ldrd_strd,
+                                                            has_armv8a));
 }
 
 bool ArmInstructionSetFeatures::Equals(const InstructionSetFeatures* other) const {
@@ -219,13 +269,26 @@
     return false;
   }
   const ArmInstructionSetFeatures* other_as_arm = other->AsArmInstructionSetFeatures();
-  return has_div_ == other_as_arm->has_div_ &&
-      has_atomic_ldrd_strd_ == other_as_arm->has_atomic_ldrd_strd_;
+  return has_div_ == other_as_arm->has_div_
+      && has_atomic_ldrd_strd_ == other_as_arm->has_atomic_ldrd_strd_
+      && has_armv8a_ == other_as_arm->has_armv8a_;
+}
+
+bool ArmInstructionSetFeatures::HasAtLeast(const InstructionSetFeatures* other) const {
+  if (kArm != other->GetInstructionSet()) {
+    return false;
+  }
+  const ArmInstructionSetFeatures* other_as_arm = other->AsArmInstructionSetFeatures();
+
+  return (has_div_ || (has_div_ == other_as_arm->has_div_))
+      && (has_atomic_ldrd_strd_ || (has_atomic_ldrd_strd_ == other_as_arm->has_atomic_ldrd_strd_))
+      && (has_armv8a_ || (has_armv8a_ == other_as_arm->has_armv8a_));
 }
 
 uint32_t ArmInstructionSetFeatures::AsBitmap() const {
-  return (has_div_ ? kDivBitfield : 0) |
-      (has_atomic_ldrd_strd_ ? kAtomicLdrdStrdBitfield : 0);
+  return (has_div_ ? kDivBitfield : 0)
+      | (has_atomic_ldrd_strd_ ? kAtomicLdrdStrdBitfield : 0)
+      | (has_armv8a_ ? kARMv8A : 0);
 }
 
 std::string ArmInstructionSetFeatures::GetFeatureString() const {
@@ -240,6 +303,11 @@
   } else {
     result += ",-atomic_ldrd_strd";
   }
+  if (has_armv8a_) {
+    result += ",armv8a";
+  } else {
+    result += ",-armv8a";
+  }
   return result;
 }
 
@@ -248,6 +316,7 @@
     const std::vector<std::string>& features, std::string* error_msg) const {
   bool has_atomic_ldrd_strd = has_atomic_ldrd_strd_;
   bool has_div = has_div_;
+  bool has_armv8a = has_armv8a_;
   for (auto i = features.begin(); i != features.end(); i++) {
     std::string feature = android::base::Trim(*i);
     if (feature == "div") {
@@ -258,13 +327,17 @@
       has_atomic_ldrd_strd = true;
     } else if (feature == "-atomic_ldrd_strd") {
       has_atomic_ldrd_strd = false;
+    } else if (feature == "armv8a") {
+      has_armv8a = true;
+    } else if (feature == "-armv8a") {
+      has_armv8a = false;
     } else {
       *error_msg = StringPrintf("Unknown instruction set feature: '%s'", feature.c_str());
       return nullptr;
     }
   }
   return std::unique_ptr<const InstructionSetFeatures>(
-      new ArmInstructionSetFeatures(has_div, has_atomic_ldrd_strd));
+      new ArmInstructionSetFeatures(has_div, has_atomic_ldrd_strd, has_armv8a));
 }
 
 }  // namespace art
diff --git a/runtime/arch/arm/instruction_set_features_arm.h b/runtime/arch/arm/instruction_set_features_arm.h
index 11f8bf0..f438a76 100644
--- a/runtime/arch/arm/instruction_set_features_arm.h
+++ b/runtime/arch/arm/instruction_set_features_arm.h
@@ -49,6 +49,8 @@
 
   bool Equals(const InstructionSetFeatures* other) const OVERRIDE;
 
+  bool HasAtLeast(const InstructionSetFeatures* other) const OVERRIDE;
+
   InstructionSet GetInstructionSet() const OVERRIDE {
     return kArm;
   }
@@ -69,6 +71,11 @@
     return has_atomic_ldrd_strd_;
   }
 
+  // Are ARMv8-A instructions available?
+  bool HasARMv8AInstructions() const {
+    return has_armv8a_;
+  }
+
   virtual ~ArmInstructionSetFeatures() {}
 
  protected:
@@ -78,19 +85,24 @@
                                  std::string* error_msg) const OVERRIDE;
 
  private:
-  ArmInstructionSetFeatures(bool has_div, bool has_atomic_ldrd_strd)
+  ArmInstructionSetFeatures(bool has_div,
+                            bool has_atomic_ldrd_strd,
+                            bool has_armv8a)
       : InstructionSetFeatures(),
-        has_div_(has_div), has_atomic_ldrd_strd_(has_atomic_ldrd_strd) {
-  }
+        has_div_(has_div),
+        has_atomic_ldrd_strd_(has_atomic_ldrd_strd),
+        has_armv8a_(has_armv8a) {}
 
   // Bitmap positions for encoding features as a bitmap.
   enum {
     kDivBitfield = 1 << 0,
     kAtomicLdrdStrdBitfield = 1 << 1,
+    kARMv8A = 1 << 2,
   };
 
   const bool has_div_;
   const bool has_atomic_ldrd_strd_;
+  const bool has_armv8a_;
 
   DISALLOW_COPY_AND_ASSIGN(ArmInstructionSetFeatures);
 };
diff --git a/runtime/arch/arm/instruction_set_features_arm_test.cc b/runtime/arch/arm/instruction_set_features_arm_test.cc
index 697ca90..6d5dd6d 100644
--- a/runtime/arch/arm/instruction_set_features_arm_test.cc
+++ b/runtime/arch/arm/instruction_set_features_arm_test.cc
@@ -31,7 +31,7 @@
   EXPECT_TRUE(krait_features->Equals(krait_features.get()));
   EXPECT_TRUE(krait_features->AsArmInstructionSetFeatures()->HasDivideInstruction());
   EXPECT_TRUE(krait_features->AsArmInstructionSetFeatures()->HasAtomicLdrdAndStrd());
-  EXPECT_STREQ("div,atomic_ldrd_strd", krait_features->GetFeatureString().c_str());
+  EXPECT_STREQ("div,atomic_ldrd_strd,-armv8a", krait_features->GetFeatureString().c_str());
   EXPECT_EQ(krait_features->AsBitmap(), 3U);
 
   // Build features for a 32-bit ARM denver processor.
@@ -40,12 +40,13 @@
   ASSERT_TRUE(denver_features.get() != nullptr) << error_msg;
 
   EXPECT_TRUE(denver_features->Equals(denver_features.get()));
-  EXPECT_TRUE(denver_features->Equals(krait_features.get()));
-  EXPECT_TRUE(krait_features->Equals(denver_features.get()));
+  EXPECT_TRUE(denver_features->HasAtLeast(krait_features.get()));
+  EXPECT_FALSE(krait_features->Equals(denver_features.get()));
+  EXPECT_FALSE(krait_features->HasAtLeast(denver_features.get()));
   EXPECT_TRUE(denver_features->AsArmInstructionSetFeatures()->HasDivideInstruction());
   EXPECT_TRUE(denver_features->AsArmInstructionSetFeatures()->HasAtomicLdrdAndStrd());
-  EXPECT_STREQ("div,atomic_ldrd_strd", denver_features->GetFeatureString().c_str());
-  EXPECT_EQ(denver_features->AsBitmap(), 3U);
+  EXPECT_STREQ("div,atomic_ldrd_strd,armv8a", denver_features->GetFeatureString().c_str());
+  EXPECT_EQ(denver_features->AsBitmap(), 7U);
 
   // Build features for a 32-bit ARMv7 processor.
   std::unique_ptr<const InstructionSetFeatures> generic_features(
@@ -57,7 +58,7 @@
   EXPECT_FALSE(krait_features->Equals(generic_features.get()));
   EXPECT_FALSE(generic_features->AsArmInstructionSetFeatures()->HasDivideInstruction());
   EXPECT_FALSE(generic_features->AsArmInstructionSetFeatures()->HasAtomicLdrdAndStrd());
-  EXPECT_STREQ("-div,-atomic_ldrd_strd", generic_features->GetFeatureString().c_str());
+  EXPECT_STREQ("-div,-atomic_ldrd_strd,-armv8a", generic_features->GetFeatureString().c_str());
   EXPECT_EQ(generic_features->AsBitmap(), 0U);
 
   // ARM6 is not a supported architecture variant.
@@ -82,21 +83,22 @@
   EXPECT_TRUE(krait_features->Equals(krait_features.get()));
   EXPECT_TRUE(krait_features->AsArmInstructionSetFeatures()->HasDivideInstruction());
   EXPECT_TRUE(krait_features->AsArmInstructionSetFeatures()->HasAtomicLdrdAndStrd());
-  EXPECT_STREQ("div,atomic_ldrd_strd", krait_features->GetFeatureString().c_str());
+  EXPECT_STREQ("div,atomic_ldrd_strd,-armv8a", krait_features->GetFeatureString().c_str());
   EXPECT_EQ(krait_features->AsBitmap(), 3U);
 
   // Build features for a 32-bit ARM processor with LPAE and div flipped.
   std::unique_ptr<const InstructionSetFeatures> denver_features(
-      base_features->AddFeaturesFromString("div,atomic_ldrd_strd", &error_msg));
+      base_features->AddFeaturesFromString("div,atomic_ldrd_strd,armv8a", &error_msg));
   ASSERT_TRUE(denver_features.get() != nullptr) << error_msg;
 
   EXPECT_TRUE(denver_features->Equals(denver_features.get()));
-  EXPECT_TRUE(denver_features->Equals(krait_features.get()));
-  EXPECT_TRUE(krait_features->Equals(denver_features.get()));
+  EXPECT_FALSE(denver_features->Equals(krait_features.get()));
+  EXPECT_TRUE(denver_features->HasAtLeast(krait_features.get()));
+  EXPECT_FALSE(krait_features->Equals(denver_features.get()));
   EXPECT_TRUE(denver_features->AsArmInstructionSetFeatures()->HasDivideInstruction());
   EXPECT_TRUE(denver_features->AsArmInstructionSetFeatures()->HasAtomicLdrdAndStrd());
-  EXPECT_STREQ("div,atomic_ldrd_strd", denver_features->GetFeatureString().c_str());
-  EXPECT_EQ(denver_features->AsBitmap(), 3U);
+  EXPECT_STREQ("div,atomic_ldrd_strd,armv8a", denver_features->GetFeatureString().c_str());
+  EXPECT_EQ(denver_features->AsBitmap(), 7U);
 
   // Build features for a 32-bit default ARM processor.
   std::unique_ptr<const InstructionSetFeatures> generic_features(
@@ -108,7 +110,7 @@
   EXPECT_FALSE(krait_features->Equals(generic_features.get()));
   EXPECT_FALSE(generic_features->AsArmInstructionSetFeatures()->HasDivideInstruction());
   EXPECT_FALSE(generic_features->AsArmInstructionSetFeatures()->HasAtomicLdrdAndStrd());
-  EXPECT_STREQ("-div,-atomic_ldrd_strd", generic_features->GetFeatureString().c_str());
+  EXPECT_STREQ("-div,-atomic_ldrd_strd,-armv8a", generic_features->GetFeatureString().c_str());
   EXPECT_EQ(generic_features->AsBitmap(), 0U);
 }
 
diff --git a/runtime/arch/arm/instruction_set_features_assembly_tests.S b/runtime/arch/arm/instruction_set_features_assembly_tests.S
index c1086df..5c7f202 100644
--- a/runtime/arch/arm/instruction_set_features_assembly_tests.S
+++ b/runtime/arch/arm/instruction_set_features_assembly_tests.S
@@ -17,22 +17,49 @@
 #include "asm_support_arm.S"
 
 .section .text
-// This function is used to check for the CPU's support for the sdiv
-// instruction at runtime.  It will either return the value 1 or
-// will cause an invalid instruction trap (SIGILL signal).  The
-// caller must arrange for the signal handler to set the r0
-// register to 0 and move the pc forward by 4 bytes (to skip
-// the invalid instruction).
+// These functions are used to check for the CPU's support for the sdiv and
+// ARMv8-A instructions at runtime. They will either return the value 1 or will
+// cause an invalid instruction trap (SIGILL signal), for which the signal handler
+// (bad_instr_handle(), in instruction_set_features_arm.cc) must arrange to set
+// the r0 register to 0 and move the pc forward by 4 bytes (to skip the invalid
+// instruction).
+// Note: For ARM T32, instructions can be either 16b or 32b, but bad_instr_handle()
+// deals only with 32b instructions for now.
+
 ENTRY artCheckForArmSdivInstruction
   mov r1,#1
-  // depending on the architecture, the assembler will not allow an
+  // Depending on the architecture, the assembler will not allow an
   // sdiv instruction, so we will have to output the bytes directly.
 
-  // sdiv r0,r1,r1 is two words: 0xfb91 0xf1f0.  We need little endian.
-  .byte 0x91,0xfb,0xf1,0xf0
+  // The T32 encoding for sdiv r0,r1,r1 is two 16bit words: 0xfb91 0xf0f1, with little endianness.
+  .byte 0x91,0xfb
+  .byte 0xf1,0xf0
 
-  // if the divide worked, r0 will have the value #1 (result of sdiv).
+  // If the divide worked, r0 will have the value #1 (result of sdiv).
   // It will have 0 otherwise (set by the signal handler)
   // the value is just returned from this function.
   bx lr
 END artCheckForArmSdivInstruction
+
+ENTRY artCheckForArmv8AInstructions
+  // Depending on the architecture, the assembler will not allow a
+  // `vrint` instruction, so we will have to output the bytes directly.
+
+  // Move `true` into the result register. The signal handler will set it to 0
+  // if execution of the instruction below fails
+  mov r0,#1
+
+  // Store S0 in the caller saved R1. If the instruction below succeeds, S0 will
+  // be clobbered but it will not be caller saved (ARM still uses soft FP).
+  vmov r1, s0
+
+  // The T32 encoding for vrinta.f32.f32 s0,s0 is two 16bit words: 0xfeb8,0x0a40, with little
+  // endianness.
+  .byte 0xb8,0xfe
+  .byte 0x40,0x0a
+
+  // Restore S0 (see above comment).
+  vmov s0, r1
+
+  bx lr
+END artCheckForArmv8AInstructions
diff --git a/runtime/arch/instruction_set_features.h b/runtime/arch/instruction_set_features.h
index b6c5c71..5f1a507 100644
--- a/runtime/arch/instruction_set_features.h
+++ b/runtime/arch/instruction_set_features.h
@@ -67,6 +67,24 @@
   // Are these features the same as the other given features?
   virtual bool Equals(const InstructionSetFeatures* other) const = 0;
 
+  // For testing purposes we want to make sure that the system we run on has at
+  // least the options we claim it has. In this cases Equals() does not
+  // suffice and will cause the test to fail, since the runtime cpu feature
+  // detection claims more capabilities then statically specified from the
+  // build system.
+  //
+  // A good example of this is the armv8 ART test target that declares
+  // "CPU_VARIANT=generic". If the generic target is specified and the code
+  // is run on a platform with enhanced capabilities, the
+  // instruction_set_features test will fail if we resort to using Equals()
+  // between statically defined cpu features and runtime cpu features.
+  //
+  // For now we default this to Equals() in case the architecture does not
+  // provide it.
+  virtual bool HasAtLeast(const InstructionSetFeatures* other) const {
+    return Equals(other);
+  }
+
   // Return the ISA these features relate to.
   virtual InstructionSet GetInstructionSet() const = 0;
 
diff --git a/runtime/arch/instruction_set_features_test.cc b/runtime/arch/instruction_set_features_test.cc
index d489392..67e2f35 100644
--- a/runtime/arch/instruction_set_features_test.cc
+++ b/runtime/arch/instruction_set_features_test.cc
@@ -52,7 +52,7 @@
         InstructionSetFeatures::FromVariant(kRuntimeISA, dex2oat_isa_variant, &error_msg));
     ASSERT_TRUE(property_features.get() != nullptr) << error_msg;
 
-    EXPECT_TRUE(property_features->Equals(instruction_set_features.get()))
+    EXPECT_TRUE(property_features->HasAtLeast(instruction_set_features.get()))
       << "System property features: " << *property_features.get()
       << "\nFeatures from build: " << *instruction_set_features.get();
   }
@@ -89,7 +89,7 @@
           base_features->AddFeaturesFromString(dex2oat_isa_features, &error_msg));
       ASSERT_TRUE(property_features.get() != nullptr) << error_msg;
 
-      EXPECT_TRUE(property_features->Equals(instruction_set_features.get()))
+      EXPECT_TRUE(property_features->HasAtLeast(instruction_set_features.get()))
       << "System property features: " << *property_features.get()
       << "\nFeatures from build: " << *instruction_set_features.get();
     }
@@ -109,7 +109,7 @@
   // Check we get the same instruction set features using /proc/cpuinfo.
   std::unique_ptr<const InstructionSetFeatures> cpuinfo_features(
       InstructionSetFeatures::FromCpuInfo());
-  EXPECT_TRUE(cpuinfo_features->Equals(instruction_set_features.get()))
+  EXPECT_TRUE(cpuinfo_features->HasAtLeast(instruction_set_features.get()))
       << "CPU Info features: " << *cpuinfo_features.get()
       << "\nFeatures from build: " << *instruction_set_features.get();
 }
@@ -124,7 +124,7 @@
 
   std::unique_ptr<const InstructionSetFeatures> cpp_features(
       InstructionSetFeatures::FromCppDefines());
-  EXPECT_TRUE(default_features->Equals(cpp_features.get()))
+  EXPECT_TRUE(cpp_features->HasAtLeast(default_features.get()))
       << "Default variant features: " << *default_features.get()
       << "\nFeatures from build: " << *cpp_features.get();
 }
@@ -143,7 +143,7 @@
   // Check we get the same instruction set features using AT_HWCAP.
   std::unique_ptr<const InstructionSetFeatures> hwcap_features(
       InstructionSetFeatures::FromHwcap());
-  EXPECT_TRUE(hwcap_features->Equals(instruction_set_features.get()))
+  EXPECT_TRUE(hwcap_features->HasAtLeast(instruction_set_features.get()))
       << "Hwcap features: " << *hwcap_features.get()
       << "\nFeatures from build: " << *instruction_set_features.get();
 }
@@ -156,7 +156,7 @@
   // Check we get the same instruction set features using assembly tests.
   std::unique_ptr<const InstructionSetFeatures> assembly_features(
       InstructionSetFeatures::FromAssembly());
-  EXPECT_TRUE(assembly_features->Equals(instruction_set_features.get()))
+  EXPECT_TRUE(assembly_features->HasAtLeast(instruction_set_features.get()))
       << "Assembly features: " << *assembly_features.get()
       << "\nFeatures from build: " << *instruction_set_features.get();
 }