ART: Propagate verifier failure types to the compilers

Add a bit-set encoding of seen failure types to the verifier and
make it available. Store this in VerifiedMethod, so that compilers
can inspect it and make choices based on failures. Rewrite the
current punting of runtime-throw errors to be at the compiler-driver
level.

Bug: 23502994
Change-Id: I1cfc7cbdf2aec1f14ba18f0169e432ba4ae16883
diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc
index 6e73ae7..3642b82 100644
--- a/compiler/dex/quick/quick_compiler.cc
+++ b/compiler/dex/quick/quick_compiler.cc
@@ -679,11 +679,8 @@
     return nullptr;
   }
 
-  if (driver->GetVerifiedMethod(&dex_file, method_idx)->HasRuntimeThrow()) {
-    return nullptr;
-  }
-
   DCHECK(driver->GetCompilerOptions().IsCompilationEnabled());
+  DCHECK(!driver->GetVerifiedMethod(&dex_file, method_idx)->HasRuntimeThrow());
 
   Runtime* const runtime = Runtime::Current();
   ClassLinker* const class_linker = runtime->GetClassLinker();
diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc
index 273b1628..8eb37cf 100644
--- a/compiler/dex/verified_method.cc
+++ b/compiler/dex/verified_method.cc
@@ -37,11 +37,21 @@
 
 namespace art {
 
+VerifiedMethod::VerifiedMethod(uint32_t encountered_error_types,
+                               bool has_runtime_throw,
+                               const SafeMap<uint32_t, std::set<uint32_t>>& string_init_pc_reg_map)
+    : encountered_error_types_(encountered_error_types),
+      has_runtime_throw_(has_runtime_throw),
+      string_init_pc_reg_map_(string_init_pc_reg_map) {
+}
+
 const VerifiedMethod* VerifiedMethod::Create(verifier::MethodVerifier* method_verifier,
                                              bool compile) {
-  std::unique_ptr<VerifiedMethod> verified_method(new VerifiedMethod);
-  verified_method->has_verification_failures_ = method_verifier->HasFailures();
-  verified_method->has_runtime_throw_ = method_verifier->HasInstructionThatWillThrow();
+  std::unique_ptr<VerifiedMethod> verified_method(
+      new VerifiedMethod(method_verifier->GetEncounteredFailureTypes(),
+                         method_verifier->HasInstructionThatWillThrow(),
+                         method_verifier->GetStringInitPcRegMap()));
+
   if (compile) {
     /* Generate a register map. */
     if (!verified_method->GenerateGcMap(method_verifier)) {
@@ -66,8 +76,6 @@
     verified_method->GenerateSafeCastSet(method_verifier);
   }
 
-  verified_method->SetStringInitPcRegMap(method_verifier->GetStringInitPcRegMap());
-
   return verified_method.release();
 }
 
diff --git a/compiler/dex/verified_method.h b/compiler/dex/verified_method.h
index f7d6d67..74fcb07 100644
--- a/compiler/dex/verified_method.h
+++ b/compiler/dex/verified_method.h
@@ -72,22 +72,25 @@
 
   // Returns true if there were any errors during verification.
   bool HasVerificationFailures() const {
-    return has_verification_failures_;
+    return encountered_error_types_ != 0;
+  }
+
+  uint32_t GetEncounteredVerificationFailures() const {
+    return encountered_error_types_;
   }
 
   bool HasRuntimeThrow() const {
     return has_runtime_throw_;
   }
 
-  void SetStringInitPcRegMap(SafeMap<uint32_t, std::set<uint32_t>>& string_init_pc_reg_map) {
-    string_init_pc_reg_map_ = string_init_pc_reg_map;
-  }
   const SafeMap<uint32_t, std::set<uint32_t>>& GetStringInitPcRegMap() const {
     return string_init_pc_reg_map_;
   }
 
  private:
-  VerifiedMethod() = default;
+  VerifiedMethod(uint32_t encountered_error_types,
+                 bool has_runtime_throw,
+                 const SafeMap<uint32_t, std::set<uint32_t>>& string_init_pc_reg_map);
 
   /*
    * Generate the GC map for a method that has just been verified (i.e. we're doing this as part of
@@ -124,12 +127,12 @@
   DequickenMap dequicken_map_;
   SafeCastSet safe_cast_set_;
 
-  bool has_verification_failures_ = false;
-  bool has_runtime_throw_ = false;
+  const uint32_t encountered_error_types_;
+  const bool has_runtime_throw_;
 
   // Copy of mapping generated by verifier of dex PCs of string init invocations
   // to the set of other registers that the receiver has been copied into.
-  SafeMap<uint32_t, std::set<uint32_t>> string_init_pc_reg_map_;
+  const SafeMap<uint32_t, std::set<uint32_t>> string_init_pc_reg_map_;
 };
 
 }  // namespace art
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index fa25a17..d2fc4ea 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -590,14 +590,18 @@
   } else if ((access_flags & kAccAbstract) != 0) {
     // Abstract methods don't have code.
   } else {
-    bool has_verified_method = driver->GetVerificationResults()
-        ->GetVerifiedMethod(method_ref) != nullptr;
+    const VerifiedMethod* verified_method =
+        driver->GetVerificationResults()->GetVerifiedMethod(method_ref);
     bool compile = compilation_enabled &&
         // Basic checks, e.g., not <clinit>.
         driver->GetVerificationResults()
             ->IsCandidateForCompilation(method_ref, access_flags) &&
         // Did not fail to create VerifiedMethod metadata.
-        has_verified_method &&
+        verified_method != nullptr &&
+        // Do not have failures that should punt to the interpreter.
+        !verified_method->HasRuntimeThrow() &&
+        (verified_method->GetEncounteredVerificationFailures() &
+            verifier::VERIFY_ERROR_FORCE_INTERPRETER) == 0 &&
         // Is eligable for compilation by methods-to-compile filter.
         driver->IsMethodToCompile(method_ref);
     if (compile) {
@@ -620,7 +624,7 @@
           method_idx,
           class_loader,
           dex_file,
-          has_verified_method
+          (verified_method != nullptr)
               ? dex_to_dex_compilation_level
               : optimizer::DexToDexCompilationLevel::kRequired);
     }
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index f6bbace..2a76991 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -795,8 +795,8 @@
                                             const DexFile& dex_file) const {
   CompilerDriver* compiler_driver = GetCompilerDriver();
   CompiledMethod* method = nullptr;
-  if (compiler_driver->IsMethodVerifiedWithoutFailures(method_idx, class_def_idx, dex_file) &&
-      !compiler_driver->GetVerifiedMethod(&dex_file, method_idx)->HasRuntimeThrow()) {
+  DCHECK(!compiler_driver->GetVerifiedMethod(&dex_file, method_idx)->HasRuntimeThrow());
+  if (compiler_driver->IsMethodVerifiedWithoutFailures(method_idx, class_def_idx, dex_file)) {
      method = TryCompile(code_item, access_flags, invoke_type, class_def_idx,
                          method_idx, jclass_loader, dex_file);
   } else {
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index efefa8b..f8b2089 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -416,6 +416,7 @@
       have_any_pending_runtime_throw_failure_(false),
       new_instance_count_(0),
       monitor_enter_count_(0),
+      encountered_failure_types_(0),
       can_load_classes_(can_load_classes),
       allow_soft_failures_(allow_soft_failures),
       need_precise_constants_(need_precise_constants),
@@ -587,6 +588,9 @@
 }
 
 std::ostream& MethodVerifier::Fail(VerifyError error) {
+  // Mark the error type as encountered.
+  encountered_failure_types_ |= (1U << static_cast<uint32_t>(error));
+
   switch (error) {
     case VERIFY_ERROR_NO_CLASS:
     case VERIFY_ERROR_NO_FIELD:
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 21f8543..d0841f0 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -67,17 +67,17 @@
  * to be rewritten to fail at runtime.
  */
 enum VerifyError {
-  VERIFY_ERROR_BAD_CLASS_HARD,  // VerifyError; hard error that skips compilation.
-  VERIFY_ERROR_BAD_CLASS_SOFT,  // VerifyError; soft error that verifies again at runtime.
+  VERIFY_ERROR_BAD_CLASS_HARD = 0,      // VerifyError; hard error that skips compilation.
+  VERIFY_ERROR_BAD_CLASS_SOFT = 1,      // VerifyError; soft error that verifies again at runtime.
 
-  VERIFY_ERROR_NO_CLASS,        // NoClassDefFoundError.
-  VERIFY_ERROR_NO_FIELD,        // NoSuchFieldError.
-  VERIFY_ERROR_NO_METHOD,       // NoSuchMethodError.
-  VERIFY_ERROR_ACCESS_CLASS,    // IllegalAccessError.
-  VERIFY_ERROR_ACCESS_FIELD,    // IllegalAccessError.
-  VERIFY_ERROR_ACCESS_METHOD,   // IllegalAccessError.
-  VERIFY_ERROR_CLASS_CHANGE,    // IncompatibleClassChangeError.
-  VERIFY_ERROR_INSTANTIATION,   // InstantiationError.
+  VERIFY_ERROR_NO_CLASS = 2,            // NoClassDefFoundError.
+  VERIFY_ERROR_NO_FIELD = 3,            // NoSuchFieldError.
+  VERIFY_ERROR_NO_METHOD = 4,           // NoSuchMethodError.
+  VERIFY_ERROR_ACCESS_CLASS = 5,        // IllegalAccessError.
+  VERIFY_ERROR_ACCESS_FIELD = 6,        // IllegalAccessError.
+  VERIFY_ERROR_ACCESS_METHOD = 7,       // IllegalAccessError.
+  VERIFY_ERROR_CLASS_CHANGE = 8,        // IncompatibleClassChangeError.
+  VERIFY_ERROR_INSTANTIATION = 9,       // InstantiationError.
   // For opcodes that don't have complete verifier support (such as lambda opcodes),
   // we need a way to continue execution at runtime without attempting to re-verify
   // (since we know it will fail no matter what). Instead, run as the interpreter
@@ -85,25 +85,12 @@
   // on the fly.
   //
   // TODO: Once all new opcodes have implemented full verifier support, this can be removed.
-  VERIFY_ERROR_FORCE_INTERPRETER,  // Skip the verification phase at runtime;
-                                   // force the interpreter to do access checks.
-                                   // (sets a soft fail at compile time).
+  VERIFY_ERROR_FORCE_INTERPRETER = 10,  // Skip the verification phase at runtime;
+                                        // force the interpreter to do access checks.
+                                        // (sets a soft fail at compile time).
 };
 std::ostream& operator<<(std::ostream& os, const VerifyError& rhs);
 
-/*
- * Identifies the type of reference in the instruction that generated the verify error
- * (e.g. VERIFY_ERROR_ACCESS_CLASS could come from a method, field, or class reference).
- *
- * This must fit in two bits.
- */
-enum VerifyErrorRefType {
-  VERIFY_ERROR_REF_CLASS  = 0,
-  VERIFY_ERROR_REF_FIELD  = 1,
-  VERIFY_ERROR_REF_METHOD = 2,
-};
-const int kVerifyErrorRefTypeShift = 6;
-
 // We don't need to store the register data for many instructions, because we either only need
 // it at branch points (for verification) or GC points and branches (for verification +
 // type-precise register analysis).
@@ -291,6 +278,10 @@
     return string_init_pc_reg_map_;
   }
 
+  uint32_t GetEncounteredFailureTypes() {
+    return encountered_failure_types_;
+  }
+
  private:
   // Private constructor for dumping.
   MethodVerifier(Thread* self, const DexFile* dex_file, Handle<mirror::DexCache> dex_cache,
@@ -753,6 +744,9 @@
   size_t new_instance_count_;
   size_t monitor_enter_count_;
 
+  // Bitset of the encountered failure types. Bits are according to the values in VerifyError.
+  uint32_t encountered_failure_types_;
+
   const bool can_load_classes_;
 
   // Converts soft failures to hard failures when false. Only false when the compiler isn't